HMIXER CMicVolume::MixDevice(HMIXER current_mix, unsigned int mix_id)
{
	HMIXER h_mixer;
	MIXERCAPS mixer_caps;

	if(mixerGetDevCaps(mix_id, &mixer_caps, sizeof(mixer_caps)) != MMSYSERR_NOERROR)
		return NULL;

    if(mixerOpen(&h_mixer, mix_id, DWORD(NULL), 0L, CALLBACK_WINDOW) != MMSYSERR_NOERROR)
		return NULL;

    if(current_mix != NULL){
		if(mixerClose(current_mix) != MMSYSERR_NOERROR)
		{
#ifdef _DEBUG
			MessageBox(NULL,"Mixer 장치가 사용중입니다.", "Mixer 열기실패", MB_ICONSTOP);
#endif
        current_mix = NULL;
		}
    }

	//char szbuf[128];

	//wsprintf(szbuf,"Mixer 장치명: %s", mixer_caps.szPname);
    //SetWindowText(g_hWnd, szbuf);			//윈도우 타이틀바에 장치명 알림

    return h_mixer;
}
Пример #2
0
//##################################################
//agenttype_mixer_menu_devices
//##################################################
void agenttype_mixer_menu_devices(Menu *menu, control *c, char *action, char *agentname, int format)
{
	//Variables
	MIXERCAPS       mixer_capabilities;
	HMIXER          mixer_handle;

	//Count the devices
	UINT count_devices = mixerGetNumDevs();
	if (count_devices < 1)
	{
		make_menuitem_nop(menu, "No audio devices present.");
		return;
	}

	//For every device...
	for (UINT device = 0; device < count_devices; device++)
	{
		//Open the mixer
		if (MMSYSERR_NOERROR == mixerOpen(  &mixer_handle, device, 0, 0, 0)
			&& MMSYSERR_NOERROR == mixerGetDevCaps(device, &mixer_capabilities, sizeof(MIXERCAPS)))
		{
			//Create a submenu for destination lines
			Menu *submenu;
			submenu = make_menu(mixer_capabilities.szPname, c);
			agenttype_mixer_menu_destlines(submenu, c, action, agentname, format, device, mixer_handle, mixer_capabilities);
			make_submenu_item(menu, mixer_capabilities.szPname, submenu);

			//Close the mixer
			mixerClose(mixer_handle);
		}
	}
	return;
}
INT32 PORT_GetPortMixerDescription(INT32 mixerIndex, PortMixerDescription* description) {
    MIXERCAPS mixerCaps;
    if (mixerGetDevCaps(mixerIndex, &mixerCaps, sizeof(MIXERCAPS)) == MMSYSERR_NOERROR) {
        strncpy(description->name, mixerCaps.szPname, PORT_STRING_LENGTH-1);
        description->name[PORT_STRING_LENGTH-1] = 0;
        sprintf(description->version, "%d.%d", (mixerCaps.vDriverVersion & 0xFF00) >> 8, mixerCaps.vDriverVersion & 0xFF);
        strncpy(description->description, "Port Mixer", PORT_STRING_LENGTH-1);
        return TRUE;
    }
Пример #4
0
BOOL
SndMixerSelect(PSND_MIXER Mixer,
               UINT MixerId)
{
    if (MixerId >= Mixer->MixersCount)
    {
        return FALSE;
    }

    SndMixerClose(Mixer);

    if (mixerOpen(&Mixer->hmx,
                  MixerId,
                  (DWORD_PTR)Mixer->hWndNotification,
                  0,
                  CALLBACK_WINDOW | MIXER_OBJECTF_MIXER) == MMSYSERR_NOERROR ||
        mixerOpen(&Mixer->hmx,
                  MixerId,
                  (DWORD_PTR)Mixer->hWndNotification,
                  0,
                  CALLBACK_WINDOW) == MMSYSERR_NOERROR ||
        mixerOpen(&Mixer->hmx,
                  MixerId,
                  0,
                  0,
                  0) == MMSYSERR_NOERROR)
    {
        if (mixerGetDevCaps(MixerId,
                            &Mixer->Caps,
                            sizeof(Mixer->Caps)) == MMSYSERR_NOERROR)
        {
            BOOL Ret = FALSE;

            Mixer->MixerId = MixerId;

            ClearMixerCache(Mixer);

            Ret = SndMixerQueryDestinations(Mixer);

            if (!Ret)
            {
                ClearMixerCache(Mixer);
            }

            return Ret;
        }
        else
        {
            mixerClose(Mixer->hmx);
        }
    }

    Mixer->hmx = NULL;
    Mixer->MixerId = NO_MIXER_SELECTED;
    return FALSE;
}
Пример #5
0
/**
 * A ridiculous amount of code to do something this simple.
 * But mixers are pretty abstract a subject, I guess...
 * (No, the API just sucks.)
 */
static int initMixer(void)
{
    MIXERCAPS   mixerCaps;
    int         num = mixerGetNumDevs(); // Number of mixer devices.

    if(initMixerOk || CommandLine_Check("-nomixer"))
        return true;

    if(verbose)
    {
        // In verbose mode, print a lot of extra information.
        Con_Message("dsWinMM::initMixer: Number of mixer devices: %i", num);
    }

    // Open the mixer device.
    res = mixerOpen(&mixer, 0, 0, 0, MIXER_OBJECTF_MIXER);
    if(res != MMSYSERR_NOERROR)
    {
        if(verbose)
            Con_Message("  Error opening mixer: Error %i", res);
        return 0;
    }

    // Get the device caps.
    mixerGetDevCaps((UINT_PTR) mixer, &mixerCaps, sizeof(mixerCaps));

    Con_Message("dsWinMM::initMixer: %s", mixerCaps.szPname);
    if(verbose)
        Con_Message("  Audio line destinations: %i",
                    mixerCaps.cDestinations);

    // Init CD mixer.
    if(verbose)
        Con_Message("Init CD audio line:");
    initMixerLine(&mixCD, MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC);
    if(verbose)
        Con_Message("Init synthesizer line:");
    initMixerLine(&mixMidi, MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER);

    // We're successful.
    initMixerOk = true;

    // Get the original mixer volume settings (restored at shutdown).
    origVol = mixer3i(MIX_MIDI, MIX_GET, MIX_VOLUME);
    origCDVol = mixer3i(MIX_CDAUDIO, MIX_GET, MIX_VOLUME);

    return true;
}
Пример #6
0
BOOL
SndMixerEnumProducts(PSND_MIXER Mixer,
                     PFNSNDMIXENUMPRODUCTS EnumProc,
                     PVOID Context)
{
    MIXERCAPS Caps;
    HMIXER hMixer;
    UINT i;
    BOOL Ret = TRUE;

    for (i = 0; i < Mixer->MixersCount; i++)
    {
        if (mixerOpen(&hMixer,
                      i,
                      0,
                      0,
                      0) == MMSYSERR_NOERROR)
        {
            if (mixerGetDevCaps(i,
                                &Caps,
                                sizeof(Caps)) == MMSYSERR_NOERROR)
            {
                if (!EnumProc(Mixer,
                              i,
                              Caps.szPname,
                              Context))
                {
                    mixerClose(hMixer);
                    Ret = FALSE;
                    break;
                }
            }
            else
            {
                DPRINT("Failed to get device capabilities for mixer id %d!\n", i);
            }
            mixerClose(hMixer);
        }
    }

    return Ret;
}
Пример #7
0
    PLUGIN_FUNCTION_ARG3(GetDeviceName,unsigned,index, char *,buffer, unsigned,bufsize)
    {
      if (buffer == NULL || bufsize < 4)
        return PluginLID_InvalidParameter;

      if (index >= 1)
        return PluginLID_NoMoreNames;

      UINT numDevs = mixerGetNumDevs();
      for(UINT i = 0; i < numDevs; i++){
        MIXERCAPS caps;
	mixerGetDevCaps(i, &caps, sizeof(caps));
        if (strstr(caps.szPname, "USB Audio") != NULL) {
          strcpy(buffer, "HID");
          return PluginLID_NoError;
        }
      }

      return PluginLID_NoMoreNames;
    }
//--------------------------------------------------------------------------------------
//       Class:  SoundResources
//      Method:  _print_dst_lines
// Description:  This function prints in the screen the source lines present
//--------------------------------------------------------------------------------------
void
SoundResources::_print_dst_lines()
{
	MIXERCAPS mixerCaps;

	mixerGetDevCaps(0,&mixerCaps, sizeof(mixerCaps));

	for (int i = 0; i < mixerCaps.cDestinations; i++)
	{
		m_mixerLine.cbStruct      = sizeof(MIXERLINE);
		m_mixerLine.dwSource      = 0;
		m_mixerLine.dwDestination = i;

		if (!(m_err = mixerGetLineInfo((HMIXEROBJ)m_MixerHandle, &m_mixerLine, MIXER_GETLINEINFOF_DESTINATION))) {
				printf("\t#%lu: %s\n", i, m_mixerLine.szName);
		}
		m_numSrc = m_mixerLine.cConnections;
		_print_src_lines();
	}
}
Пример #9
0
/* If the PROP_DEVICE_NAME is set, find the mixer related to device;
 * otherwise we get the default input mixer. */
static gboolean
gst_directsound_src_mixer_find (GstDirectSoundSrc * dsoundsrc,
    MIXERCAPS * mixer_caps)
{
  MMRESULT mmres;
  guint i, num_mixers;

  num_mixers = mixerGetNumDevs ();
  for (i = 0; i < num_mixers; i++) {
    mmres = mixerOpen (&dsoundsrc->mixer, i, 0L, 0L,
        MIXER_OBJECTF_MIXER | MIXER_OBJECTF_WAVEIN);

    if (mmres != MMSYSERR_NOERROR)
      continue;

    mmres = mixerGetDevCaps (GPOINTER_TO_UINT (dsoundsrc->mixer),
        mixer_caps, sizeof (MIXERCAPS));

    if (mmres != MMSYSERR_NOERROR) {
      mixerClose (dsoundsrc->mixer);
      continue;
    }

    /* Get default mixer */
    if (dsoundsrc->device_name == NULL) {
      GST_DEBUG ("Got default input mixer: %s", mixer_caps->szPname);
      return TRUE;
    }

    if (g_strstr_len (dsoundsrc->device_name, -1, mixer_caps->szPname) != NULL) {
      GST_DEBUG ("Got requested input mixer: %s", mixer_caps->szPname);
      return TRUE;
    }

    /* Wrong mixer */
    mixerClose (dsoundsrc->mixer);
  }

  GST_DEBUG ("Can't find input mixer");
  return FALSE;
}
BOOL CMicVolume::GetDevice()
{
	MIXERCAPS mixer_caps;
	MIXERLINE mixer_line;
    if(mixerGetDevCaps((unsigned int)h_mix, &mixer_caps, sizeof(mixer_caps)) != MMSYSERR_NOERROR)
	{
#ifdef _DEBUG
		MessageBox(NULL, "등록된 장치의 정보를 읽을 수 없습니다.", "시스템 정보 오류", MB_ICONSTOP);
#endif
		mixer_caps.cDestinations = 0;
		return FALSE;
    }

    for(unsigned int i = 0; i < mixer_caps.cDestinations; i++){
        mixer_line.cbStruct = sizeof(mixer_line);
        mixer_line.dwDestination = i;
        if(mixerGetLineInfo((HMIXEROBJ)h_mix, &mixer_line, MIXER_GETLINEINFOF_DESTINATION) != MMSYSERR_NOERROR)
			continue;

		if(MIXERLINE_COMPONENTTYPE_DST_SPEAKERS == mixer_line.dwComponentType){
			m_WavOut.nDeviceNum = (short)mixer_line.cConnections;
			m_WavOut.nIndex = (short)i;
			m_WavOut.nLineID = mixer_line.dwLineID;
			strcpy(m_WavOut.DeviceName, mixer_line.szName);
			//GetDeviceCtrl(&m_WavOut, &m_ctlSpeaker);
		} else if(MIXERLINE_COMPONENTTYPE_DST_WAVEIN == mixer_line.dwComponentType){
			m_WavIn.nDeviceNum = (short)mixer_line.cConnections;
			m_WavIn.nIndex = (short)i;
			m_WavIn.nLineID = mixer_line.dwLineID;
			strcpy(m_WavIn.DeviceName, mixer_line.szName);
		}
	}

	GetMicDevice();	

	if(VolDevice(&m_MicIn) < 0)
	{
		return FALSE;
	}
	return TRUE;
}
Пример #11
0
static void winsnd_detect(MSSndCardManager *m){
    MMRESULT mr = NOERROR;
    unsigned int nInDevices = waveInGetNumDevs ();
    unsigned int item;
	char pcmdev[1024];
	char mixdev[1024];

    for (item = 0; item < nInDevices; item++)
    {
        WAVEINCAPS caps;
        mr = waveInGetDevCaps (item, &caps, sizeof (WAVEINCAPS));
        if (mr == MMSYSERR_NOERROR)
        {
            MSSndCard *card;
	        snprintf(pcmdev,sizeof(pcmdev),"%s",caps.szPname);
	        snprintf(mixdev,sizeof(mixdev),"%s",caps.szPname);
            if (item == 0)
            {
		        card=winsnd_card_new(pcmdev,mixdev, item-1);
		        ms_snd_card_manager_add_card(m,card);
            }
			card=winsnd_card_new(pcmdev,mixdev, item);
			ms_snd_card_manager_add_card(m,card);
        }
    }
#if 0
	nInDevices = mixerGetNumDevs ();
    for (item = 0; item < nInDevices; item++)
    {
        MIXERCAPS caps;
        mr = mixerGetDevCaps (item, &caps, sizeof (MIXERCAPS));
        if (mr == MMSYSERR_NOERROR)
        {
	        snprintf(pcmdev,sizeof(pcmdev),"%s",caps.szPname);
	        snprintf(mixdev,sizeof(mixdev),"%s",caps.szPname);
        }
    }
#endif
}
void CMicVolume::GetMicDevice()
{
	MIXERCAPS mixer_caps;
	MIXERLINE mixer_line;
    mixerGetDevCaps((unsigned int)h_mix, &mixer_caps, sizeof(mixer_caps));

	for(int i = 0; i < m_WavIn.nDeviceNum; i++){
        mixer_line.cbStruct = sizeof(mixer_line);
        mixer_line.dwDestination = m_WavIn.nIndex;
        mixer_line.dwSource = i;

        if(mixerGetLineInfo((HMIXEROBJ)h_mix, &mixer_line, MIXER_GETLINEINFOF_SOURCE) != MMSYSERR_NOERROR)
			continue;

		if(mixer_line.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE)
		{
			m_MicIn.nIndex = (short)i;
			m_MicIn.nLineID = mixer_line.dwLineID;
			GetDeviceCtrl(&m_MicIn);
			break;
		}
	}
}
Пример #13
0
unsigned int iKX::get_winmm_device(int what,int subdevice)
{
 UINT ret=0xffffffff;

 const char *device=get_winmm_name((what==KX_WINMM_MIXER)?KX_WINMM_WAVE:what);
 if(device==0)
  return ret;

 if(subdevice==0)
 {
      switch(what)
      {
       case KX_WINMM_MIXER:
             if(mixerGetNumDevs()) // else causes unsigned infinite loop
               for(UINT i=0;i<mixerGetNumDevs();i++)
               {
                MIXERCAPS caps;
                if(mixerGetDevCaps(i,&caps,sizeof(caps))==MMSYSERR_NOERROR)
                {
                  if(strcmp(caps.szPname,device)==NULL)
                  {
                   ret=i;
                   break;
                  }
                } else break;
               }
             break;
       case KX_WINMM_WAVE:
       case KX_WINMM_WAVE23:
       case KX_WINMM_WAVE45:
       case KX_WINMM_WAVE67:
       case KX_WINMM_WAVEHQ:
             if(waveInGetNumDevs()) // else causes unsigned infinite loop
               for(UINT i=0;i<waveInGetNumDevs();i++)
               {
                WAVEINCAPS caps;
                if(waveInGetDevCaps(i,&caps,sizeof(caps))==MMSYSERR_NOERROR)
                {
                  if(strcmp(caps.szPname,device)==NULL)
                  {
                   ret=i;
                   break;
                  }
                } else break;
               }
             break;
       case KX_WINMM_SYNTH:
       case KX_WINMM_SYNTH2:
       case KX_WINMM_UART:
       case KX_WINMM_UART2:
       case KX_WINMM_CTRL:
             if(midiInGetNumDevs()) // else causes unsigned infinite loop
               for(UINT i=0;i<midiInGetNumDevs();i++)
               {
                MIDIINCAPS caps;
                if(midiInGetDevCaps(i,&caps,sizeof(caps))==MMSYSERR_NOERROR)
                {
                  if(strcmp(caps.szPname,device)==NULL)
                  {
                   ret=i;
                   break;
                  }
                } else break;
               }
             break;
      }
 }
 else
 {
      switch(what)
      {
       case KX_WINMM_MIXER:
            debug("kxapi: incorrect get_winmm call(mixer)\n");
            break;
       case KX_WINMM_WAVE:
       case KX_WINMM_WAVE23:
       case KX_WINMM_WAVE45:
       case KX_WINMM_WAVE67:
       case KX_WINMM_WAVEHQ:
             if(waveOutGetNumDevs()) // else causes unsigned infinite loop
               for(UINT i=0;i<waveOutGetNumDevs();i++)
               {
                WAVEOUTCAPS caps;
                if(waveOutGetDevCaps(i,&caps,sizeof(caps))==MMSYSERR_NOERROR)
                {
                  if(strcmp(caps.szPname,device)==NULL)
                  {
                   ret=i;
                   break;
                  }
                } else break;
               }
             break;
       case KX_WINMM_SYNTH:
       case KX_WINMM_SYNTH2:
       case KX_WINMM_UART:
       case KX_WINMM_UART2:
       case KX_WINMM_CTRL:
             if(midiOutGetNumDevs()) // else causes unsigned infinite loop
               for(UINT i=0;i<midiOutGetNumDevs();i++)
               {
                MIDIOUTCAPS caps;
                if(midiOutGetDevCaps(i,&caps,sizeof(caps))==MMSYSERR_NOERROR)
                {
                  if(strcmp(caps.szPname,device)==NULL)
                  {
                   ret=i;
                   break;
                  }
                } else break;
               }
             break;
      }
 }
 return ret;
}
Пример #14
0
int iKX::init_winmm()
{
    // check for Vista and Windows7:
    OSVERSIONINFOEX ver;
    memset(&ver,0,sizeof(ver));
    ver.dwOSVersionInfoSize=sizeof(ver);

    if(GetVersionEx((OSVERSIONINFO *)&ver)) // ok?
    {
     if(ver.dwPlatformId==VER_PLATFORM_WIN32_NT && ver.dwMajorVersion>=6)
     {
      debug("iKX(): init_winmm: mixers are not supported. Vista+ workaround\n");
      return 0;
     }
    }

    int ret=-1;

    for(unsigned int i=0;i<mixerGetNumDevs();i++)
    {
        HMIXER mixer=0;
        int to_close=1;

        // open mixer
        if(mixerOpen(&mixer,i,0,0,MIXER_OBJECTF_MIXER)!=MMSYSERR_NOERROR)
        {
            debug("iKX(): mixerOpen failed [%x]\n",GetLastError());
            continue; // next mixer
        }
        // get mixer caps
        MIXERCAPS mc; memset(&mc,0,sizeof(mc));
        if(mixerGetDevCaps(i,&mc,sizeof(mc))!=MMSYSERR_NOERROR)
        {
            debug("iKX(): mixerGetDevCaps() failed [%x]\n",GetLastError());
            mixerClose(mixer);
            continue;
        }

        // debug("iKX(): init_winmm: looking for '%s': '%s' [#%d out of %d]\n",wave_name,mc.szPname,i,mixerGetNumDevs());

        if(strncmp(mc.szPname,wave_name,strlen(wave_name)-4)!=NULL) // yes, this should be 'wave_name', not 'mixer_name';
        { mixerClose(mixer); continue; }

        if(strcmp(mc.szPname,wave_name)==0)
         { mixer_handler[0]=(uintptr_t)mixer; to_close=0; ret=0; }
        else
         if(strcmp(mc.szPname,wave23_name)==0)
          { mixer_handler[1]=(uintptr_t)mixer; to_close=0;  ret=0; }
         else
          if(strcmp(mc.szPname,wave45_name)==0)
           { mixer_handler[2]=(uintptr_t)mixer; to_close=0;  ret=0; }
          else
           if(strcmp(mc.szPname,wave67_name)==0)
            { mixer_handler[3]=(uintptr_t)mixer; to_close=0;  ret=0; }
           else
           if(strcmp(mc.szPname,waveHQ_name)==0)
             { mixer_handler[4]=(uintptr_t)mixer; to_close=0; ret=0; }
            else
            {
             debug("kXAPI: WARNING: incorrect init_winmm() code: %s\n",mc.szPname);
            }
        if(to_close)
         mixerClose(mixer);
//      break;
    }
    if(ret==-1)
    {
     debug("iKX(): init_winmm: number of mixers: %d\n",mixerGetNumDevs());
    }

    return ret;
}
bool setVolume(DWORD volume) {
	HMIXER mixer;
	int mixerNum ;//总的混音器数量
	bool is_find_mic;

	//获取当前系统总的混音器数量
	mixerNum= mixerGetNumDevs(); 	
	is_find_mic = false;
	for(int i=0;i<mixerNum;i++)
	{

		if (mixerOpen(&mixer, i, 0, 0, 0) != MMSYSERR_NOERROR) {   //这里修改麦克风音量是没有问题的,第一个你要找出那个混音器是麦克风啊
			printf("mixer open failed ...\n");
			MessageBoxW(NULL, L"Error: mixerOpen()", NULL, MB_ICONHAND);
			return false;
		}

		// Get the line info
		MIXERCAPS mixcaps;
		MIXERLINE mixerLine;
		mixerGetDevCaps(i, &mixcaps, sizeof(MIXERCAPS));
		mixerLine.cbStruct = sizeof(MIXERLINE);
		mixerLine.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;  //用MIXERLINE_COMPONENTTYPE_DST_WAVEIN 作参数可得到所有录音的Connection
		mixerLine.dwSource = 0;
		mixerLine.dwDestination = 0;

		if (mixerGetLineInfo(reinterpret_cast<HMIXEROBJ>(mixer), &mixerLine, MIXER_GETLINEINFOF_SOURCE)
			!= MMSYSERR_NOERROR) {
				MessageBoxW(NULL, L"Error: mixerGetLineInfo()", NULL, MB_ICONHAND);
				return false;
		}

		if(mixerLine.dwComponentType !=MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE ){
			printf("i = %d ,this device is not microphone \n" ,i);
			mixerClose(mixer);
			continue;
		}

		is_find_mic = true;
		printf("mixerLine.dwComponentType = %xH ,MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE = %xH \n" ,mixerLine.dwComponentType ,MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE);

		//下面的代码只有是耳麦的时候才去执行
		// Get control for mixerline
		MIXERCONTROL mixerCtrl;
		MIXERLINECONTROLS mixerLineCtrl;

		mixerLineCtrl.cbStruct = sizeof(MIXERLINECONTROLS);
		mixerLineCtrl.dwLineID = mixerLine.dwLineID;
		mixerLineCtrl.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
		mixerLineCtrl.cControls = 1;
		mixerLineCtrl.pamxctrl = &mixerCtrl;
		mixerLineCtrl.cbmxctrl = sizeof(MIXERCONTROL);
		mixerLineCtrl.cControls = 5;

		if (mixerGetLineControls(reinterpret_cast<HMIXEROBJ>(mixer), &mixerLineCtrl, MIXER_GETLINECONTROLSF_ONEBYTYPE)
			!= MMSYSERR_NOERROR) {
				MessageBoxW(NULL, L"Error: mixerGetLineControls()", NULL, MB_ICONHAND);
				return false;
		}

		// Volume..
		MIXERCONTROLDETAILS mixerCtrlDetails;
		MIXERCONTROLDETAILS_UNSIGNED mixerCtrlDetailsUnsigned;

		mixerCtrlDetailsUnsigned.dwValue = volume;		//volume size 
		mixerCtrlDetails.dwControlID = mixerCtrl.dwControlID;
		mixerCtrlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
		mixerCtrlDetails.cMultipleItems = 0;
		mixerCtrlDetails.paDetails = &mixerCtrlDetailsUnsigned;
		mixerCtrlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
		mixerCtrlDetails.cChannels = 1;

		if (mixerSetControlDetails(reinterpret_cast<HMIXEROBJ>(mixer), &mixerCtrlDetails, MIXER_OBJECTF_HMIXER | MIXER_GETCONTROLDETAILSF_VALUE)
			!= MMSYSERR_NOERROR) {
				MessageBoxW(NULL, L"Error: mixerSetControlDetails()", NULL, MB_ICONHAND);
				return false;
		}

		mixerClose(mixer);
	}//for loop

	return is_find_mic;
}
Пример #16
0
VOID
InitVolumeControls(HWND hwndDlg, PGLOBAL_DATA pGlobalData)
{
    UINT NumMixers;
    MIXERCAPS mxc;
    TCHAR szNoDevices[256];

    CheckDlgButton(hwndDlg,
                   IDC_ICON_IN_TASKBAR,
                   GetSystrayVolumeIconState() ? BST_CHECKED : BST_UNCHECKED);

    LoadString(hApplet, IDS_NO_DEVICES, szNoDevices, _countof(szNoDevices));

    NumMixers = mixerGetNumDevs();
    if (!NumMixers)
    {
        EnableWindow(GetDlgItem(hwndDlg, IDC_VOLUME_TRACKBAR), FALSE);
        EnableWindow(GetDlgItem(hwndDlg, IDC_MUTE_CHECKBOX),   FALSE);
        EnableWindow(GetDlgItem(hwndDlg, IDC_ADVANCED_BTN),    FALSE);
        EnableWindow(GetDlgItem(hwndDlg, IDC_SPEAKER_VOL_BTN), FALSE);
        EnableWindow(GetDlgItem(hwndDlg, IDC_ADVANCED2_BTN),   FALSE);
        SendDlgItemMessage(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconNoHW);
        SetDlgItemText(hwndDlg, IDC_DEVICE_NAME, szNoDevices);
        return;
    }

    if (mixerOpen(&pGlobalData->hMixer, 0, PtrToUlong(hwndDlg), 0, MIXER_OBJECTF_MIXER | CALLBACK_WINDOW) != MMSYSERR_NOERROR)
    {
        MessageBox(hwndDlg, _T("Cannot open mixer"), NULL, MB_OK);
        return;
    }

    ZeroMemory(&mxc, sizeof(MIXERCAPS));
    if (mixerGetDevCaps(PtrToUint(pGlobalData->hMixer), &mxc, sizeof(MIXERCAPS)) != MMSYSERR_NOERROR)
    {
        MessageBox(hwndDlg, _T("mixerGetDevCaps failed"), NULL, MB_OK);
        return;
    }

    GetMuteControl(pGlobalData);
    GetMuteState(pGlobalData);
    if (pGlobalData->muteVal)
    {
        SendDlgItemMessage(hwndDlg, IDC_MUTE_CHECKBOX, BM_SETCHECK, (WPARAM)BST_CHECKED, (LPARAM)0);
        SendDlgItemMessage(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconMuted);
    }
    else
    {
        SendDlgItemMessage(hwndDlg, IDC_MUTE_CHECKBOX, BM_SETCHECK, (WPARAM)BST_UNCHECKED, (LPARAM)0);
        SendDlgItemMessage(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconUnMuted);
    }

    GetVolumeControl(pGlobalData);
    GetVolumeValue(pGlobalData);

    SendDlgItemMessage(hwndDlg, IDC_DEVICE_NAME, WM_SETTEXT, 0, (LPARAM)mxc.szPname);
    SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETRANGE, (WPARAM)TRUE,
        (LPARAM)MAKELONG(pGlobalData->volumeMinimum, pGlobalData->volumeMaximum/VOLUME_DIVIDER));
    SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETPAGESIZE, (WPARAM)FALSE, (LPARAM)1);
    SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETSEL, (WPARAM)FALSE,
        (LPARAM)MAKELONG(pGlobalData->volumeMinimum, pGlobalData->volumeValue/VOLUME_DIVIDER));
    SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)pGlobalData->volumeValue/VOLUME_DIVIDER);
}
Пример #17
0
int SelectRecordingLine(unsigned int nMixerId, unsigned int nComponentID, char *user_mixer_line, unsigned int nLeftVolume, unsigned int nRightVolume)
{
	// check windows version because this can't work on VIsta and Windows 7
	OSVERSIONINFO osvi;
	ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
	osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
	if(GetVersionEx(&osvi) == 0)
		return 1;	// don't fail, just exit function


	// check if we have Vista or WIndows /
	if(osvi.dwMajorVersion > 5)
		return 1;




	unsigned int dwVolumeControlID = 0;
	unsigned int dwControlType = 0;
	unsigned int dwSelectControlID = 0;
	unsigned int dwMultipleItems = 0;
	unsigned int dwIndex = 0xFFFFFFFF;
	HMIXER hMixer = NULL;
	MIXERCAPS mxcaps;
	::ZeroMemory(&mxcaps, sizeof(MIXERCAPS));

	if(nComponentID >= WAVE_IN_NUM)
		return 0;

	// open mixer

	if(mixerOpen(&hMixer, (unsigned int) nMixerId, 0, 0, MIXER_OBJECTF_HWAVEIN) != MMSYSERR_NOERROR)
		return 0;

	if(mixerGetDevCaps((unsigned int) hMixer, &mxcaps, sizeof(MIXERCAPS)) != MMSYSERR_NOERROR)
	{
		::mixerClose(hMixer);
		return 0;
	}


	// get control we need
	if (hMixer == NULL)
		return 0;

	// get dwLineID
	MIXERLINE mxl;
	mxl.cbStruct = sizeof(MIXERLINE);
	mxl.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
	if (mixerGetLineInfo((HMIXEROBJ) hMixer, &mxl, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_COMPONENTTYPE) != MMSYSERR_NOERROR)
	{
		mixerClose(hMixer);
		return 0;
	}


	// get dwControlID
	MIXERCONTROL mxc;
	MIXERLINECONTROLS mxlc;
	dwControlType = MIXERCONTROL_CONTROLTYPE_MIXER;
	mxlc.cbStruct = sizeof(MIXERLINECONTROLS);
	mxlc.dwLineID = mxl.dwLineID;
	mxlc.dwControlType = dwControlType;
	mxlc.cControls = 1;
	mxlc.cbmxctrl = sizeof(MIXERCONTROL);
	mxlc.pamxctrl = &mxc;
	if (mixerGetLineControls((HMIXEROBJ) hMixer, &mxlc, MIXER_OBJECTF_HMIXER | MIXER_GETLINECONTROLSF_ONEBYTYPE) != MMSYSERR_NOERROR)
	{
		// no mixer, try MUX
		dwControlType = MIXERCONTROL_CONTROLTYPE_MUX;
		mxlc.cbStruct = sizeof(MIXERLINECONTROLS);
		mxlc.dwLineID = mxl.dwLineID;
		mxlc.dwControlType = dwControlType;
		mxlc.cControls = 1;
		mxlc.cbmxctrl = sizeof(MIXERCONTROL);
		mxlc.pamxctrl = &mxc;
		if (mixerGetLineControls((HMIXEROBJ) hMixer, &mxlc, MIXER_OBJECTF_HMIXER | MIXER_GETLINECONTROLSF_ONEBYTYPE) != MMSYSERR_NOERROR)
		{
			mixerClose(hMixer);
			return 0;
		}
	}


	// store dwControlID, cMultipleItems
	dwSelectControlID = mxc.dwControlID;
	dwMultipleItems = mxc.cMultipleItems;



	if (dwMultipleItems == 0)
	{
		mixerClose(hMixer);
		return 0;
	}


	DWORD dwControlID = 0;

	DWORD dwLine = 0;

	// get the index of the specified Select control
	MIXERCONTROLDETAILS_LISTTEXT *pmxcdSelectText = new MIXERCONTROLDETAILS_LISTTEXT[dwMultipleItems];
	DWORD dwi = 0;
	if (pmxcdSelectText != NULL)
	{
		MIXERCONTROLDETAILS mxcd;
		mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
		mxcd.dwControlID = dwSelectControlID;
		mxcd.cChannels = 1;
		mxcd.cMultipleItems = dwMultipleItems;
		mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXT);
		mxcd.paDetails = pmxcdSelectText;
		if (mixerGetControlDetails((HMIXEROBJ) hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_GETCONTROLDETAILSF_LISTTEXT) == MMSYSERR_NOERROR)
		{
			for (dwi = 0; dwi < dwMultipleItems; dwi++)
			{
				// get the line information
				MIXERLINE mxl;
				mxl.cbStruct = sizeof(MIXERLINE);
				mxl.dwLineID = pmxcdSelectText[dwi].dwParam1;
				if (mixerGetLineInfo((HMIXEROBJ) hMixer, &mxl, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_LINEID) == MMSYSERR_NOERROR
						&& mxl.dwComponentType == g_wavein_id[nComponentID].id)
				{
					// check specified user name
					if(user_mixer_line)
					{
						if(strcmp(user_mixer_line, mxl.szShortName) != 0 && strcmp(user_mixer_line, mxl.szName) != 0)
							continue;
					}

					// found, dwi is the index.
				
					dwIndex = dwi;
					dwLine = pmxcdSelectText[dwIndex].dwParam1;
					break;
				}
			}

			if (dwi >= dwMultipleItems)
			{

				if(user_mixer_line)
				{
					// could not find it using line IDs, some mixer drivers have
					// different meaning for MIXERCONTROLDETAILS_LISTTEXT.dwParam1.
					// let's try comparing the item names.
					for (dwi = 0; dwi < dwMultipleItems; dwi++)
					{
						if (stricmp(pmxcdSelectText[dwi].szName, user_mixer_line) == 0)
						{
							// found, dwi is the index.
							dwIndex = dwi;
							dwLine = pmxcdSelectText[dwIndex].dwParam1;
							break;
						}
					}


					// error
					if (dwi >= dwMultipleItems)
					{
						mixerClose(hMixer);
						delete []pmxcdSelectText;
						return 0;
					}

				}
			}


			if (dwi >= dwMultipleItems)
			{
				// could not find it using line IDs, some mixer drivers have
				// different meaning for MIXERCONTROLDETAILS_LISTTEXT.dwParam1.
				// let's try comparing the item names.
				for (dwi = 0; dwi < dwMultipleItems; dwi++)
				{
					if (stricmp(pmxcdSelectText[dwi].szName, g_wavein_id[nComponentID].string_name1) == 0)
					{
						// found, dwi is the index.
						dwIndex = dwi;
						dwLine = pmxcdSelectText[dwIndex].dwParam1;
						break;
					}
				}
			}


			if (dwi >= dwMultipleItems)
			{
				// could not find it using line IDs, some mixer drivers have
				// different meaning for MIXERCONTROLDETAILS_LISTTEXT.dwParam1.
				// let's try comparing the item names.
				for (dwi = 0; dwi < dwMultipleItems; dwi++)
				{
					if (stricmp(pmxcdSelectText[dwi].szName, g_wavein_id[nComponentID].string_name2) == 0)
					{
						// found, dwi is the index.
						dwIndex = dwi;
						dwLine = pmxcdSelectText[dwIndex].dwParam1;
						break;
					}
				}
			}

			if (dwi >= dwMultipleItems)
			{
				// could not find it using line IDs, some mixer drivers have
				// different meaning for MIXERCONTROLDETAILS_LISTTEXT.dwParam1.
				// let's try comparing the item names.
				for (dwi = 0; dwi < dwMultipleItems; dwi++)
				{
					if (stricmp(pmxcdSelectText[dwi].szName, g_wavein_id[nComponentID].string_name3) == 0)
					{
						// found, dwi is the index.
						dwIndex = dwi;
						dwLine = pmxcdSelectText[dwIndex].dwParam1;
						break;
					}
				}
			}
	
		}

		delete []pmxcdSelectText;
	}


	// select component

	if (dwIndex >= dwMultipleItems)
	{
		mixerClose(hMixer);
		return 0;
	}





	// get the line information


	int ret = 0;
	// get all the values first
	MIXERCONTROLDETAILS_BOOLEAN *pmxcdSelectValue = new MIXERCONTROLDETAILS_BOOLEAN[dwMultipleItems];

	if (pmxcdSelectValue != NULL)
	{
		// select control
		MIXERCONTROLDETAILS mxcd;
		mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
		mxcd.dwControlID = dwSelectControlID;
		mxcd.cChannels = 1;
		mxcd.cMultipleItems = dwMultipleItems;
		mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
		mxcd.paDetails = pmxcdSelectValue;
		if (mixerGetControlDetails((HMIXEROBJ) hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_GETCONTROLDETAILSF_VALUE) == MMSYSERR_NOERROR)
		{
			// MUX restricts the line selection to one source line at a time.
			if (dwControlType == MIXERCONTROL_CONTROLTYPE_MUX)
				ZeroMemory(pmxcdSelectValue, dwMultipleItems * sizeof(MIXERCONTROLDETAILS_BOOLEAN));

			// set the value
			pmxcdSelectValue[dwIndex].fValue = 1;

			mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
			mxcd.dwControlID = dwSelectControlID;
			mxcd.cChannels = 1;
			mxcd.cMultipleItems = dwMultipleItems;
			mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
			mxcd.paDetails = pmxcdSelectValue;
			if (mixerSetControlDetails((HMIXEROBJ) hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE) == MMSYSERR_NOERROR)
			{
				ret = 1;
			}
		}

		delete []pmxcdSelectValue;
	}


	// set volume, if volume is larger than 100 skip volume change

	if(nLeftVolume <= 100 && nRightVolume <= 100)
	{

		mxl.cbStruct = sizeof(MIXERLINE);
		mxl.dwLineID = dwLine;
		if (mixerGetLineInfo((HMIXEROBJ) hMixer, &mxl, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_LINEID) == MMSYSERR_NOERROR)
		{				
			mxlc.cbStruct = sizeof(MIXERLINECONTROLS);
			mxlc.dwLineID = dwLine;
			mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
			mxlc.cbmxctrl = sizeof(MIXERCONTROL);
			mxlc.pamxctrl = &mxc;
			if (mixerGetLineControls((HMIXEROBJ) hMixer, &mxlc, MIXER_OBJECTF_HMIXER | MIXER_GETLINECONTROLSF_ONEBYTYPE) == MMSYSERR_NOERROR)
			{	

				if(nLeftVolume > 100)
					nLeftVolume = 100;
				
				if(nRightVolume > 100)
					nRightVolume = 100;

				MIXERCONTROLDETAILS_UNSIGNED mxcdVolume[2] = { mxc.Bounds.dwMaximum * nLeftVolume / 100, mxc.Bounds.dwMaximum * nRightVolume / 100};
				MIXERCONTROLDETAILS mxcd;
				mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
				mxcd.dwControlID = mxc.dwControlID;
				mxcd.cChannels = mxl.cChannels;
				mxcd.cMultipleItems = 0;
				mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
				mxcd.paDetails = mxcdVolume;
				if (mixerSetControlDetails((HMIXEROBJ) hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE) != MMSYSERR_NOERROR)
				{
					// error, can't set volume
				}
			}
			else
			{
				// error can't set volume
			}
		}
	}

	mixerClose(hMixer);
	return ret;
}
Пример #18
0
BOOL CALLBACK MixerPlus_OptionsDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static PMIXERPLUS_INFO pinfo = NULL;

	UINT i;
	MIXERCAPS mxcaps;

	switch(uMsg)
	{
		case WM_COMMAND:

			switch(LOWORD(wParam))
			{
				case IDOK:

					pinfo->dwMixerID = (DWORD) SendDlgItemMessage(hDlg,
																	IDC_DEVICE,
																	CB_GETCURSEL, 0, 0);

					if(IsDlgButtonChecked(hDlg, IDC_MODE_PLAYBACK) == BST_CHECKED)
						pinfo->mode = MIXERPLUS_MODE_PLAYBACK;

					else pinfo->mode = MIXERPLUS_MODE_RECORDING;

					EndDialog(hDlg, IDOK);
					return TRUE;

				case IDCANCEL:

					EndDialog(hDlg, IDCANCEL);
					return TRUE;
			}

			break;

		case WM_INITDIALOG:

			pinfo = (PMIXERPLUS_INFO) lParam;

			for(i = 0; i < mixerGetNumDevs(); i++)
				if(!MMERROR(mixerGetDevCaps(i, &mxcaps, sizeof(mxcaps))))
				{
					SendDlgItemMessage(hDlg,
										IDC_DEVICE,
										CB_ADDSTRING,
										0,
										(LPARAM) mxcaps.szPname);
				}

			SendDlgItemMessage(hDlg,
								IDC_DEVICE,
								CB_SETCURSEL,
								pinfo->dwMixerID,
								0);

			if(pinfo->mode == MIXERPLUS_MODE_PLAYBACK)
				CheckDlgButton(hDlg, IDC_MODE_PLAYBACK, BST_CHECKED);

			else CheckDlgButton(hDlg, IDC_MODE_RECORDING, BST_CHECKED);

			return TRUE;
	}

	return FALSE;
}
Пример #19
0
//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_COMMAND	- process the application menu
//  WM_PAINT	- Paint the main window
//  WM_DESTROY	- post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;

	switch (message)
	{
	case WM_CREATE: 
	  {
	    RECT rc;
	    GetWindowRect(hWnd, &rc);
            hwndList = CreateWindow(TEXT("listbox"), NULL, WS_CHILD | WS_VISIBLE,
              0, 0, rc.right-rc.left, rc.bottom-rc.top,
              hWnd, (HMENU)1, hInst, NULL);
	    break;
	  }
	case WM_COMMAND:
		wmId    = LOWORD(wParam);
		wmEvent = HIWORD(wParam);
		// Parse the menu selections:
		switch (wmId)
		{
		case IDM_ABOUT:
		  {
		    //DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
		    SendMessage(hwndList, LB_RESETCONTENT, 0, 0);
		    int numDevs = mixerGetNumDevs();
		    if (numDevs == 0) {
		      MessageBox(hWnd, TEXT("No audio mixer devices found."), NULL, MB_OK);
		      break;
		    }
#define MSG_LEN 1024
		    TCHAR msg[MSG_LEN+1];
		    TCHAR lpszComponent[MSG_LEN];
		    UINT nDevId = 0, uDest, uConnect, uConnections;
		    MIXERCAPS mxcaps;
		    MMRESULT rc;
		    HMIXER hmx = NULL;
		    for (int i=0; i<numDevs; i++) {
		      mixerGetDevCaps(i, &mxcaps, sizeof(MIXERCAPS));
		      wsprintf(msg, TEXT("Device name: %s\t#Destinations: %ld"), mxcaps.szPname, mxcaps.cDestinations);
		      SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)msg);
		      rc = mixerOpen(&hmx, i, (DWORD_PTR)hWnd, NULL, CALLBACK_WINDOW | MIXER_OBJECTF_MIXER);
		      if (rc != MMSYSERR_NOERROR) {
		        MessageBox(hWnd, TEXT("Error opening mixer deivce"), NULL, MB_OK);
			mixerClose(hmx);
		        continue;
		      }
		      lstrcpy(msg, TEXT("Type\tComponent\tName\tLine ID\tFlags\tCtls\tConnections"));
		      SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)msg);
		      MIXERLINE mxl;
		      for (uDest = 0; uDest < mxcaps.cDestinations; uDest++) {
			mxl.cbStruct = sizeof(mxl);
			mxl.dwDestination = uDest;
			rc = mixerGetLineInfo((HMIXEROBJ)hmx, &mxl, MIXER_GETLINEINFOF_DESTINATION);
			if (rc != MMSYSERR_NOERROR) {
			  wsprintf(msg, TEXT("mixerGetLineInfo(dst=%u) failed. rc=%u!"), uDest, rc);
			  MessageBox(hWnd, msg, NULL, MB_OK);
			  continue;
			}
			GetComponentType(&mxl, lpszComponent);
			wsprintf(msg, TEXT("%s\t%-25s\t%-25s\t%.08lXh\t%.08lXh\t%lu\t%lu"), 
			  (MIXERLINE_LINEF_ACTIVE & mxl.fdwLine) 
			    ? TEXT("Dest Active") : TEXT("Dest Inactive"),
			  lpszComponent, mxl.szName, mxl.dwLineID, mxl.fdwLine, mxl.cControls, mxl.cConnections);
			SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)msg);
			uConnections = mxl.cConnections;
			for (uConnect = 0; uConnect < uConnections; uConnect++) {
			  mxl.cbStruct = sizeof(mxl);
			  mxl.dwDestination = uDest;
			  mxl.dwSource = uConnect;
			  rc = mixerGetLineInfo((HMIXEROBJ)hmx, &mxl, MIXER_GETLINEINFOF_SOURCE);
			  if (rc != MMSYSERR_NOERROR) {
			    wsprintf(msg, TEXT("mixerGetLineInfo(src=%u) failed. rc=%u!"), uConnect, rc);
			    MessageBox(hWnd, msg, NULL, MB_OK);
			    continue;
			  }
			  GetComponentType(&mxl, lpszComponent);
			  wsprintf(msg, TEXT("%s\t%-25s\t%-25s\t%.08lXh\t%.08lXh\t%lu\t%lu"), 
			    (MIXERLINE_LINEF_ACTIVE & mxl.fdwLine) 
			      ? TEXT("Src Active") : TEXT("Src Inactive"),
			    lpszComponent, mxl.szName, mxl.dwLineID, mxl.fdwLine, mxl.cControls, mxl.cConnections);
			  SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)msg);
			}
		        SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)TEXT(""));
		      }
		    }
		    break;
		  }
		case IDM_EXIT:
			DestroyWindow(hWnd);
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
		break;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		// TODO: Add any drawing code here...
		EndPaint(hWnd, &ps);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}
void Mixer::getPortsInfo() {
  MIXERCAPS mc;
  mixerGetDevCaps((UINT)hMixer, &mc, sizeof mc);

  MIXERLINE mlt;
  unsigned i;
  for (i = 0; i < mc.cDestinations; ++i) {
    memset(&mlt, 0, sizeof mlt);
    mlt.cbStruct = sizeof mlt;
    mlt.dwDestination = i;
    if (mixerGetLineInfo((HMIXEROBJ)hMixer, &mlt, MIXER_GETLINEINFOF_DESTINATION) != MMSYSERR_NOERROR) continue;
    if (mlt.dwLineID == dwRecLineID) break; // this is the destination we're interested in
  }
  ports = new AudioInputPort[mlt.cConnections];

  numPorts = mlt.cConnections;
  for (i = 0; i < numPorts; ++i) {
    MIXERLINE mlc;
    memcpy(&mlc, &mlt, sizeof mlc);
    mlc.dwSource = i;
    mixerGetLineInfo((HMIXEROBJ)hMixer, &mlc, MIXER_GETLINEINFOF_SOURCE/*|MIXER_OBJECTF_HMIXER*/);
    ports[i].tag = mlc.dwLineID;
	ports[i].dwComponentType = mlc.dwComponentType;
#ifdef UNICODE
    wcstombs(ports[i].name, mlc.szName, MIXER_LONG_NAME_CHARS);
#else
    strncpy(ports[i].name, mlc.szName, MIXER_LONG_NAME_CHARS);
#endif
  }

  // Make the microphone the first port in the list:
  for (i = 1; i < numPorts; ++i) {
#ifdef OLD_MICROPHONE_TESTING_CODE
    if (_strnicmp("mic", ports[i].name, 3) == 0 ||
	_strnicmp("mik", ports[i].name, 3) == 0) {
#else
	if (ports[i].dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE) {
#endif
      AudioInputPort tmp = ports[0];
      ports[0] = ports[i];
      ports[i] = tmp;
    }
  }
}

Boolean Mixer::enableInputPort(unsigned portIndex, char const*& errReason, MMRESULT& errCode) {
  errReason = NULL; // unless there's an error
  AudioInputPort& port = ports[portIndex];

  MIXERCONTROL mc;
  mc.cMultipleItems = 1; // in case it doesn't get set below
  MIXERLINECONTROLS mlc;
#if 0 // the following doesn't seem to be needed, and can fail:
  mlc.cbStruct = sizeof mlc;
  mlc.pamxctrl = &mc;
  mlc.cbmxctrl = sizeof (MIXERCONTROL);
  mlc.dwLineID = port.tag;
  mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
  if ((errCode = mixerGetLineControls((HMIXEROBJ)hMixer, &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE/*|MIXER_OBJECTF_HMIXER*/)) != MMSYSERR_NOERROR) {
    errReason = "mixerGetLineControls()";
    return False;
  }
#endif

  MIXERLINE ml;
  memset(&ml, 0, sizeof (MIXERLINE));
  ml.cbStruct = sizeof (MIXERLINE);
  ml.dwLineID = port.tag;
  if ((errCode = mixerGetLineInfo((HMIXEROBJ)hMixer, &ml, MIXER_GETLINEINFOF_LINEID)) != MMSYSERR_NOERROR) {
    errReason = "mixerGetLineInfo()1";
    return False;
  }

  

  #ifdef UNICODE
    wchar_t portname[MIXER_LONG_NAME_CHARS+1];
    wcsncpy(portname, ml.szName, MIXER_LONG_NAME_CHARS);
  #else
    char portname[MIXER_LONG_NAME_CHARS+1];
    strncpy(portname, ml.szName, MIXER_LONG_NAME_CHARS);
  #endif

  memset(&ml, 0, sizeof (MIXERLINE));
  ml.cbStruct = sizeof (MIXERLINE);
  ml.dwLineID = dwRecLineID;
  if ((errCode = mixerGetLineInfo((HMIXEROBJ)hMixer, &ml, MIXER_GETLINEINFOF_LINEID/*|MIXER_OBJECTF_HMIXER*/)) != MMSYSERR_NOERROR) {
    errReason = "mixerGetLineInfo()2";
    return False;
  }

  // Get Mixer/MUX control information (need control id to set and get control details)
  mlc.cbStruct = sizeof mlc;
  mlc.dwLineID = ml.dwLineID;
  mlc.cControls = 1;
  mc.cbStruct = sizeof mc; // Needed???#####
  mc.dwControlID = 0xDEADBEEF; // For testing #####
  mlc.pamxctrl = &mc;
  mlc.cbmxctrl = sizeof mc;
  mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_MUX; // Single Select
  if ((errCode = mixerGetLineControls((HMIXEROBJ)hMixer, &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE/*|MIXER_OBJECTF_HMIXER*/)) != MMSYSERR_NOERROR) {
    mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_MIXER; // Multiple Select
    mixerGetLineControls((HMIXEROBJ)hMixer, &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE/*|MIXER_OBJECTF_HMIXER*/);
  }

  unsigned matchLine = 0;
  if (mc.cMultipleItems > 1) {
    // Before getting control, we need to know which line to grab.
    // We figure this out by listing the lines, and comparing names:
    MIXERCONTROLDETAILS mcd;
    mcd.cbStruct = sizeof mcd;
    mcd.cChannels = ml.cChannels;
    mcd.cMultipleItems = mc.cMultipleItems;
    MIXERCONTROLDETAILS_LISTTEXT* mcdlText = new MIXERCONTROLDETAILS_LISTTEXT[mc.cMultipleItems];
    mcd.cbDetails = sizeof (MIXERCONTROLDETAILS_LISTTEXT);
    mcd.paDetails = mcdlText;

    if (mc.dwControlID != 0xDEADBEEF) { // we know the control id for real
      mcd.dwControlID = mc.dwControlID;
      if ((errCode = mixerGetControlDetails((HMIXEROBJ)hMixer, &mcd, MIXER_GETCONTROLDETAILSF_LISTTEXT/*|MIXER_OBJECTF_HMIXER*/)) != MMSYSERR_NOERROR) {
	delete[] mcdlText;
	errReason = "mixerGetControlDetails()1";
	return False;
      }
    } else {
      // Hack: We couldn't find a MUX or MIXER control, so try to guess the control id:
      for (mc.dwControlID = 0; mc.dwControlID < 32; ++mc.dwControlID) {
	mcd.dwControlID = mc.dwControlID;
	if ((errCode = mixerGetControlDetails((HMIXEROBJ)hMixer, &mcd, MIXER_GETCONTROLDETAILSF_LISTTEXT/*|MIXER_OBJECTF_HMIXER*/)) == MMSYSERR_NOERROR) break;
      }
      if (mc.dwControlID == 32) { // unable to guess mux/mixer control id
	delete[] mcdlText;
	errReason = "mixerGetControlDetails()2";
	return False;
      }
    }

    #ifdef UNICODE
    for (unsigned i = 0; i < mcd.cMultipleItems; ++i) {
        if (wcscmp(mcdlText[i].szName, portname) == 0) {
	    matchLine = i;
	    break;
        }
    }
    #else
    for (unsigned i = 0; i < mcd.cMultipleItems; ++i) {
        if (strcmp(mcdlText[i].szName, portname) == 0) {
	    matchLine = i;
	    break;
        }
    }
    #endif
    
    delete[] mcdlText;
  }

  // Now get control itself:
  MIXERCONTROLDETAILS mcd;
  mcd.cbStruct = sizeof mcd;
  mcd.dwControlID = mc.dwControlID;
  mcd.cChannels = ml.cChannels;
  mcd.cMultipleItems = mc.cMultipleItems;
  MIXERCONTROLDETAILS_BOOLEAN* mcdbState = new MIXERCONTROLDETAILS_BOOLEAN[mc.cMultipleItems];
  mcd.paDetails = mcdbState;
  mcd.cbDetails = sizeof (MIXERCONTROLDETAILS_BOOLEAN);

  if ((errCode = mixerGetControlDetails((HMIXEROBJ)hMixer, &mcd, MIXER_GETCONTROLDETAILSF_VALUE/*|MIXER_OBJECTF_HMIXER*/)) != MMSYSERR_NOERROR) {
    delete[] mcdbState;
    errReason = "mixerGetControlDetails()3";
    return False;
  }

  for (unsigned j = 0; j < mcd.cMultipleItems; ++j) {
    mcdbState[j].fValue = (j == matchLine);
  }

  if ((errCode = mixerSetControlDetails((HMIXEROBJ)hMixer, &mcd, MIXER_OBJECTF_HMIXER)) != MMSYSERR_NOERROR) {
    delete[] mcdbState;
    errReason = "mixerSetControlDetails()";
    return False;
  }
  delete[] mcdbState;

  return True;
}
Пример #21
0
LRESULT CALLBACK MixerPlus_MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static HMIXER hMixer = NULL;
	static DWORD dwLines = 0;						// Must always count plinfo correctly
	static PMIXERPLUS_INFO pinfo = NULL;
	static PMIXERPLUS_LINE_INFO plinfo = NULL;		// Array
	static HWND hCFader = NULL;

	static DWORD dwControlFlags = 0;
	static MIXERLINE mxlineDest;
	static MIXERPLUS_CONTROL_FEED feed;

	UINT i, j;
	RECT rcCtrl, rcCFader, rcWnd;
	SCROLLINFO si;
	MIXERCAPS mxcaps;
	MIXERLINE mxlineSrc;
	MIXERLINECONTROLS mxlctls;
	MIXERCONTROL mxctl;
	MIXERCONTROLDETAILS mxcdtls;
	TCHAR szTitle[MIXERPLUS_MAX_TITLE];

	switch(uMsg)
	{
		case MM_MIXM_LINE_CHANGE:

			for(i = 0; i < dwLines; i++)
				if(((DWORD) lParam) == plinfo[i].dwLineID)
				{
					SendMessage(plinfo[i].hCtrl, uMsg, wParam, lParam);
					return 0;
				}

			break;

		case MM_MIXM_CONTROL_CHANGE:

			for(i = 0; i < dwLines; i++)
			{
				/* Check if child contains control for which this message is intended */
				if(((plinfo[i].dwControlFlags & MIXERPLUS_LINE_CONTROL_VOLUME)
					&& ((DWORD) lParam) == plinfo[i].volume.dwID)
					|| ((plinfo[i].dwControlFlags & MIXERPLUS_LINE_CONTROL_MUTE)
					&& ((DWORD) lParam) == plinfo[i].mute.dwID))
				{
					SendMessage(plinfo[i].hCtrl, uMsg, wParam, lParam);
					return 0;
				}
			}

			if((dwControlFlags & MIXERPLUS_LINE_CONTROL_FEED)
				&& (((DWORD) lParam) == feed.dwID))
			{
				mxlctls.cbStruct = sizeof(mxlctls);
				mxlctls.cbmxctrl = sizeof(mxctl);
				mxlctls.dwControlID = feed.dwID;
				mxlctls.pamxctrl = &mxctl;

				if(MMERROR(mixerGetLineControls((HMIXEROBJ) hMixer,
													&mxlctls,
													MIXER_GETLINECONTROLSF_ONEBYID)))
				{
					return 0;
				}

				if(feed.dwSources != mxctl.cMultipleItems)
				{
					/* Reallocate */
					HeapFree(pinfo->hHeap, 0, feed.pmxcdtls_lt);
					HeapFree(pinfo->hHeap, 0, feed.pmxcdtls_b);

					feed.pmxcdtls_lt =
						(LPMIXERCONTROLDETAILS_LISTTEXT) HeapAlloc(pinfo->hHeap, 0,
							mxctl.cMultipleItems * sizeof(MIXERCONTROLDETAILS_LISTTEXT));

					feed.pmxcdtls_b =
						(LPMIXERCONTROLDETAILS_BOOLEAN) HeapAlloc(pinfo->hHeap, 0,
							mxctl.cMultipleItems * sizeof(MIXERCONTROLDETAILS_BOOLEAN));

					if(!feed.pmxcdtls_lt || !feed.pmxcdtls_b)
					{
						dwControlFlags &= ~MIXERPLUS_LINE_CONTROL_FEED;
						MixerPlus_ErrorBox(hWnd, TEXT("Cannot allocate memory. Check available memory."));
						DestroyWindow(hWnd);
						return 0;
					}

					feed.dwSources = mxctl.cMultipleItems;
				}

				mxcdtls.cbStruct = sizeof(mxcdtls);
				mxcdtls.cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXT);
				mxcdtls.dwControlID = feed.dwID;
				mxcdtls.cMultipleItems = feed.dwSources;
				mxcdtls.cChannels = 1;
				mxcdtls.paDetails = feed.pmxcdtls_lt;

				if(MMERROR(mixerGetControlDetails((HMIXEROBJ) hMixer,
													&mxcdtls,
													MIXER_GETCONTROLDETAILSF_LISTTEXT)))
				{
					return 0;
				}

				mxcdtls.cbStruct = sizeof(mxcdtls);
				mxcdtls.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
				mxcdtls.dwControlID = feed.dwID;
				mxcdtls.cMultipleItems = feed.dwSources;
				mxcdtls.cChannels = 1;
				mxcdtls.paDetails = feed.pmxcdtls_b;

				if(MMERROR(mixerGetControlDetails((HMIXEROBJ) hMixer,
													&mxcdtls,
													MIXER_GETCONTROLDETAILSF_VALUE)))
				{
					return 0;
				}

				for(i = 0; i < dwLines; i++)
				for(j = 0; j < feed.dwSources; j++)
					if(plinfo[i].dwLineID == feed.pmxcdtls_lt[j].dwParam1)
						CheckDlgButton(plinfo[i].hCtrl,
										IDB_POWER,
										feed.pmxcdtls_b[j].fValue ? BST_CHECKED : BST_UNCHECKED);
			}

			break;

		case WM_HSCROLL:

			/* Get scroll position */
			si.cbSize = sizeof(si);
			si.fMask = SIF_ALL;
			GetScrollInfo(hWnd, SB_HORZ, &si);

			/* Adjust */
			switch(LOWORD(wParam))
			{
				case SB_THUMBTRACK: si.nPos = si.nTrackPos; break;
				case SB_LINELEFT:
				case SB_PAGELEFT:
					si.nPos--; break;
				case SB_LINERIGHT:
				case SB_PAGERIGHT:
					si.nPos++; break;
				case SB_LEFT: si.nPos = si.nMin; break;
				case SB_RIGHT: si.nPos = si.nMax; break;
			}

			SetScrollInfo(hWnd, SB_HORZ, &si, TRUE);

			/* Allow for adjustments by Windows */
			si.cbSize = sizeof(si);
			si.fMask = SIF_POS;
			GetScrollInfo(hWnd, SB_HORZ, &si);

			/* Move child windows */
			for(i = 0; i < dwLines; i++)
			{
				GetWindowRect(plinfo[i].hCtrl, &rcCtrl);
				MoveWindow(plinfo[i].hCtrl,
							(i - si.nPos) * (rcCtrl.right - rcCtrl.left), 0,
							(rcCtrl.right - rcCtrl.left), (rcCtrl.bottom - rcCtrl.top),
							TRUE);
			}

			return TRUE;

		case WM_COMMAND:

			/* Passed from crossfader */
			if(LOWORD(wParam) == IDB_FADE && HIWORD(wParam) == BN_CLICKED)
			{
				/* Get indices */
				i = (DWORD) SendDlgItemMessage(hCFader,
												IDC_SOURCE,
												CB_GETCURSEL,
												0, 0);

				j = (DWORD) SendDlgItemMessage(hCFader,
												IDC_TARGET,
												CB_GETCURSEL,
												0, 0);

				/* Do nothing if source is target */
				if(i == j)
					return TRUE;

				/* Start faders */
				SendMessage(plinfo[i].hCtrl,
								WM_COMMAND,
								MAKEWPARAM(IDB_FADEOUT, BN_CLICKED),
								(LPARAM) GetDlgItem(plinfo[i].hCtrl, IDB_FADEOUT));

				SendMessage(plinfo[j].hCtrl,
								WM_COMMAND,
								MAKEWPARAM(IDB_FADEIN, BN_CLICKED),
								(LPARAM) GetDlgItem(plinfo[j].hCtrl, IDB_FADEIN));

				return TRUE;
			}

			/* Passed from feed button */
			else if((dwControlFlags & MIXERPLUS_LINE_CONTROL_FEED)
					&& LOWORD(wParam) == IDB_POWER && HIWORD(wParam) == BN_CLICKED)
			{
				mxcdtls.cbStruct = sizeof(mxcdtls);
				mxcdtls.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
				mxcdtls.dwControlID = feed.dwID;
				mxcdtls.cMultipleItems = feed.dwSources;
				mxcdtls.cChannels = 1;
				mxcdtls.paDetails = feed.pmxcdtls_b;

				for(i = 0; i < dwLines; i++)
					if(((HWND) lParam) == GetDlgItem(plinfo[i].hCtrl, IDB_POWER))
					{
						for(j = 0; j < feed.dwSources; j++)
							if(plinfo[i].dwLineID == feed.pmxcdtls_lt[j].dwParam1)
							{
								ZeroMemory(feed.pmxcdtls_b, feed.dwSources * sizeof(MIXERCONTROLDETAILS_BOOLEAN));
								feed.pmxcdtls_b[j].fValue = !(IsDlgButtonChecked(plinfo[i].hCtrl, IDB_POWER) == BST_CHECKED);

								mixerSetControlDetails((HMIXEROBJ) hMixer,
														&mxcdtls,
														MIXER_SETCONTROLDETAILSF_VALUE);

								return TRUE;
							}

						break;
					}

				return TRUE;
			}

			switch(LOWORD(wParam))
			{
				case ID_FILE_EXIT:

					SendMessage(hWnd, WM_CLOSE, 0, 0);
					return 0;

				case ID_TOOLS_OPTIONS:

					/* Show options dialog box */
					if(DialogBoxParam(pinfo->hInstance,
										MAKEINTRESOURCE(IDD_OPTIONS),
										hWnd,
										(DLGPROC) MixerPlus_OptionsDlgProc,
										(LPARAM) pinfo) != IDOK)
					{
						return TRUE;
					}

					/* Destroy everything */
					for(i = 0; i < dwLines; i++)
						DestroyWindow(plinfo[i].hCtrl);

					if(hCFader)
					{
						DestroyWindow(hCFader);
						hCFader = NULL;
					}

					if(dwControlFlags & MIXERPLUS_LINE_CONTROL_FEED)
					{
						dwControlFlags &= ~MIXERPLUS_LINE_CONTROL_FEED;
						HeapFree(pinfo->hHeap, 0, feed.pmxcdtls_lt);
						HeapFree(pinfo->hHeap, 0, feed.pmxcdtls_b);
					}

					if(plinfo)
					{
						HeapFree(pinfo->hHeap, 0, plinfo);
						plinfo = NULL;
						dwLines = 0;
					}

					if(hMixer)
					{
						mixerClose(hMixer);
						hMixer = NULL;
					}

					/* Recreate */
					SendMessage(hWnd,
								WM_CREATE,
								0,
								0);		// Do not need to set this since pinfo not NULL

					return 0;

				case ID_HELP_ABOUT:

					MessageBox(hWnd,
								TEXT("Mixer Plus v0.2\n\nWritten by John Peloquin"),
								TEXT("Mixer Plus"),
								MB_ICONINFORMATION);

					return 0;
			}

			break;

		case WM_CREATE:

			/* NOTE: This message is also simulated when the user changes options */

			/* Get init info if we do not aleady have it */
			if(!pinfo)
				pinfo = (PMIXERPLUS_INFO)(((LPCREATESTRUCT) lParam)->lpCreateParams);

			/* Open mixer device */
			if(MMERROR(mixerOpen(&hMixer,
									pinfo->dwMixerID,
									(DWORD_PTR) hWnd, 0,
									MIXER_OBJECTF_MIXER | CALLBACK_WINDOW)))
			{
				MixerPlus_ErrorBox(hWnd, TEXT("Cannot open mixer device."));
				return 0;
			}

			if(!MMERROR(mixerGetDevCaps((UINT) hMixer, &mxcaps, sizeof(mxcaps))))
			{
				if(SUCCEEDED(StringCchPrintf(szTitle,
												LENGTHOF(szTitle),
												TEXT("Mixer Plus [%s - %s]"),
												mxcaps.szPname,
												(pinfo->mode == MIXERPLUS_MODE_PLAYBACK) ? TEXT("Playback") : TEXT("Recording"))))
				{
					SetWindowText(hWnd, szTitle);
				}
			}

			/* Get mixer destination line info */
			mxlineDest.cbStruct = sizeof(mxlineDest);
			mxlineDest.dwComponentType = (pinfo->mode == MIXERPLUS_MODE_PLAYBACK) ?
											MIXERLINE_COMPONENTTYPE_DST_SPEAKERS :
											MIXERLINE_COMPONENTTYPE_DST_WAVEIN;

			if(MMERROR(mixerGetLineInfo((HMIXEROBJ) hMixer,
											&mxlineDest,
											MIXER_GETLINEINFOF_COMPONENTTYPE)))
			{
				mixerClose(hMixer);
				MixerPlus_ErrorBox(hWnd, TEXT("Cannot open mixer destination line."));
				return 0;
			}

			/* The output destination line also has volume and other controls, so
				we wish to treat it like a source line. We do not do this for the
				recording destination line */
			dwLines = mxlineDest.cConnections
						+ ((pinfo->mode == MIXERPLUS_MODE_PLAYBACK) ? 1 : 0);

			/* Allocate info block memory for destination line and its associated source lines */
			if(!(plinfo = HeapAlloc(pinfo->hHeap,
										HEAP_ZERO_MEMORY,	// This is necessary!
										dwLines * sizeof(MIXERPLUS_LINE_INFO))))
			{
				mixerClose(hMixer);
				MixerPlus_ErrorBox(hWnd, TEXT("Cannot allocate memory. Check available memory."));
				return -1;
			}

			/* Create crossfader */
			if(dwLines > 0)
			{
				hCFader = CreateDialog(pinfo->hInstance,
											MAKEINTRESOURCE(IDD_CROSSFADER),
											hWnd,
											(DLGPROC) MixerPlus_CFaderDlgProc);
			}

			if(pinfo->mode == MIXERPLUS_MODE_PLAYBACK)
			{
				/* Put output destination line info at beginning of array */
				i = 0;
				plinfo[i].mode = pinfo->mode;
				plinfo[i].hHeap = pinfo->hHeap;
				plinfo[i].hMixer = hMixer;
				plinfo[i].dwLineID = mxlineDest.dwLineID;
				plinfo[i].dwChannels = mxlineDest.cChannels;
				StringCchCopy(plinfo[i].szName,
								LENGTHOF(plinfo[i].szName),
								mxlineDest.szName);

				/* Add to crossfader lists */
				SendDlgItemMessage(hCFader,
									IDC_SOURCE,
									CB_ADDSTRING,
									0,
									(LPARAM) plinfo[i].szName);

				SendDlgItemMessage(hCFader,
									IDC_TARGET,
									CB_ADDSTRING,
									0,
									(LPARAM) plinfo[i].szName);

				/* Create its child window */
				if(plinfo[i].hCtrl = CreateDialogParam(pinfo->hInstance,
														MAKEINTRESOURCE(IDD_MIXERCTRL),
														hWnd,
														(DLGPROC) MixerPlus_CtrlDlgProc,
														(LPARAM) &plinfo[i]))
				{
					ShowWindow(plinfo[i].hCtrl, SW_SHOW);
				}
			}

			/* Get ahold of selection control for recording destination line */
			else
			{
				mxlctls.cbStruct = sizeof(mxlctls);
				mxlctls.cbmxctrl = sizeof(mxctl);
				mxlctls.dwLineID = mxlineDest.dwLineID;
				mxlctls.dwControlType = MIXERCONTROL_CONTROLTYPE_MUX;
				mxlctls.pamxctrl = &mxctl;

				if(!MMERROR(mixerGetLineControls((HMIXEROBJ) hMixer,
													&mxlctls,
													MIXER_GETLINECONTROLSF_ONEBYTYPE)))
				{
					feed.pmxcdtls_lt =
						(LPMIXERCONTROLDETAILS_LISTTEXT) HeapAlloc(pinfo->hHeap, 0,
							mxctl.cMultipleItems * sizeof(MIXERCONTROLDETAILS_LISTTEXT));

					feed.pmxcdtls_b =
						(LPMIXERCONTROLDETAILS_BOOLEAN) HeapAlloc(pinfo->hHeap, 0,
							mxctl.cMultipleItems * sizeof(MIXERCONTROLDETAILS_BOOLEAN));

					if(!feed.pmxcdtls_lt || !feed.pmxcdtls_b)
					{
						mixerClose(hMixer);
						MixerPlus_ErrorBox(hWnd, TEXT("Cannot allocate memory. Check available memory."));
						return -1;
					}

					dwControlFlags |= MIXERPLUS_LINE_CONTROL_FEED;
					feed.dwID = mxctl.dwControlID;
					feed.dwSources = mxctl.cMultipleItems;
				}
			}

			/* Get source line info and create child windows */
			for(i = 0; i < mxlineDest.cConnections; i++)
			{
				j = (pinfo->mode == MIXERPLUS_MODE_PLAYBACK) ? i + 1 : i;

				mxlineSrc.cbStruct = sizeof(mxlineSrc);
				mxlineSrc.dwDestination = mxlineDest.dwDestination;
				mxlineSrc.dwSource = i;
				mixerGetLineInfo((HMIXEROBJ) hMixer, &mxlineSrc, MIXER_GETLINEINFOF_SOURCE);

				plinfo[j].mode = pinfo->mode;
				plinfo[j].hHeap = pinfo->hHeap;
				plinfo[j].hMixer = hMixer;
				plinfo[j].dwLineID = mxlineSrc.dwLineID;
				plinfo[j].dwChannels = mxlineSrc.cChannels;
				StringCchCopy(plinfo[j].szName,
								LENGTHOF(plinfo[j].szName),
								mxlineSrc.szName);

				/* Add to crossfader lists */
				SendDlgItemMessage(hCFader,
									IDC_SOURCE,
									CB_ADDSTRING,
									0,
									(LPARAM) plinfo[j].szName);

				SendDlgItemMessage(hCFader,
									IDC_TARGET,
									CB_ADDSTRING,
									0,
									(LPARAM) plinfo[j].szName);

				if(plinfo[j].hCtrl = CreateDialogParam(pinfo->hInstance,
														MAKEINTRESOURCE(IDD_MIXERCTRL),
														hWnd,
														(DLGPROC) MixerPlus_CtrlDlgProc,
														(LPARAM) &plinfo[j]))
				{
					if(dwControlFlags & MIXERPLUS_LINE_CONTROL_FEED)
					{
						SetDlgItemText(plinfo[j].hCtrl, IDB_POWER, TEXT("Feed"));
						EnableWindow(GetDlgItem(plinfo[j].hCtrl, IDB_POWER), TRUE);
					}

					GetWindowRect(plinfo[j].hCtrl, &rcCtrl);
					MoveWindow(plinfo[j].hCtrl,
								j * (rcCtrl.right - rcCtrl.left), 0,
								rcCtrl.right - rcCtrl.left, rcCtrl.bottom - rcCtrl.top,
								FALSE);

					ShowWindow(plinfo[j].hCtrl, SW_SHOW);
				}
			}

			/* Adjust window */
			if(dwLines > 0 && hCFader)
			{
				/* Simulate feed control update if necessary */
				if(dwControlFlags & MIXERPLUS_LINE_CONTROL_FEED)
					SendMessage(hWnd,
								MM_MIXM_CONTROL_CHANGE,
								(WPARAM) hMixer,
								(LPARAM) feed.dwID);

				/* Initialize crossfader */
				SendDlgItemMessage(hCFader,
									IDC_SOURCE,
									CB_SETCURSEL,
									0, 0);

				SendDlgItemMessage(hCFader,
									IDC_TARGET,
									CB_SETCURSEL,
									0, 0);

				GetWindowRect(hCFader, &rcCFader);
				MoveWindow(hCFader,
							0, rcCtrl.bottom - rcCtrl.top,
							rcCFader.right - rcCFader.left, rcCFader.bottom - rcCFader.top,
							FALSE);

				ShowWindow(hCFader, SW_SHOW);

				/* Resize main window */
				rcWnd.left = 0;
				rcWnd.top = 0;
				rcWnd.right = MIXERPLUS_LINES_PER_VIEW * (rcCtrl.right - rcCtrl.left);
				rcWnd.bottom = (rcCtrl.bottom - rcCtrl.top) + (rcCFader.bottom - rcCFader.top);
				AdjustWindowRect(&rcWnd, WS_CAPTION, TRUE);
				rcWnd.bottom += GetSystemMetrics(SM_CYHSCROLL);

				SetWindowPos(hWnd,
								HWND_TOP,
								0, 0,
								rcWnd.right - rcWnd.left,
								rcWnd.bottom - rcWnd.top,
								SWP_NOMOVE);

				/* Set scroll range and position */
				si.cbSize = sizeof(si);
				si.fMask = SIF_RANGE | SIF_POS;
				si.nMin = 0;
				si.nMax = max(0, dwLines - MIXERPLUS_LINES_PER_VIEW);
				si.nPos = 0;
				SetScrollInfo(hWnd, SB_HORZ, &si, TRUE);
			}

			return 0;

		case WM_DESTROY:

			if(dwControlFlags & MIXERPLUS_LINE_CONTROL_FEED)
			{
				HeapFree(pinfo->hHeap, 0, feed.pmxcdtls_lt);
				HeapFree(pinfo->hHeap, 0, feed.pmxcdtls_b);
			}

			/* BUG FIX: intermittent access violation on exit.
				
				Do not free plinfo here, as the child windows
				reference it during their WM_DESTROY processing,
				occurs after this (see MSDN).
				
				This is not a huge leak, as the heap is destroyed
				immediately after the message loop.
			*/

			/*
			if(plinfo)
				HeapFree(pinfo->hHeap, 0, plinfo);
			*/

			if(hMixer)
				mixerClose(hMixer);

			/* Kill message loop */
			PostQuitMessage(0);
			return 0;
	}

	/* Pass message to default handler */
	return DefWindowProc(hWnd, uMsg, wParam, lParam);
}