聲霸卡混音器的程式設計,我也找了好久,一直沒找到寫得比較好的。
自己的了解能力也不夠,英語就太差了。是以一直沒弄太清楚。
自從在網上發現這篇後,有茅塞頓開的感覺,寫得真不錯。
這篇原文,發表在sina的blog上,但是源blog已經被删除了(奇怪)
我都是用baidu的快照看到的。
混音器的api程式設計資料
2006-08-16 00:45:45
大 中 小
有關混音器程式設計在百度上幾乎找不到幾個網頁包含比較清楚的資料
檢視msdn上面也是僅有的一點介紹,搞了一整天,找了些零碎的代碼,看起來巨複雜
還好沒有放棄,最好在google上找了一些資料
看過以後才明白了一些,其實結構也不算複雜,隻是自己的了解力和資料都太少
下面是自己整理的,可能有不準确的地方
//----------------------------------------------------------------
基本概念:audio line
每個混音裝置(mixer device)包含一系列的音頻線路(audio lines),
一條音頻線路可能是目的線路(destination line)或者源線路(source line),
一條目的線路可能和一或多條源線路綁定(attach)在一起,
每一條(目的或源)音頻線路和一些控制(control)聯系起來,
這些控制承載着所聯系的線路的設定,
并且能夠使使用者通過與其互動進而達到設定音頻系統參數的目的
注意以上所說的control不是windows的control,隻是線路資訊的一種載體,
這種control沒有圖形界面
了解destination:places that audio goes
了解source:The audio that goes to the destinations must come from somewhere
舉個例子,一個喇叭線路代表一個目的線路,于是這條線路擁有相應的控制(喇叭本身的參數)與它聯系在一起,比如音量控制,靜音,低音,顫抖,等,同時一個喇叭(目的)線路會有一或多條源線路(輸入喇叭的信号)同它綁定(attach),比如輔助,線路輸入,合成器,CD音頻,等等,這些源線路相應綁定一些控制來設定擷取它們的行為。
//-------------------------------------------------------------------------------------------------
下面通過windows自帶的混音器程式來了解音頻線路的概念
輕按兩下windows工作列位于桌面右下角的圖示,或在系統目錄找到sndvol32.exe,即可打開windows自帶的混音器檢視設定程式,點選菜單上的選項-屬性,打開屬性視窗
在屬性視窗中我們可以選擇在主視窗中顯示并通路的為播放或者錄音,在這裡可以把這兩個選項作為兩條目的線路,它們相應的控制也可以下面的選擇框中的多選框中選擇是否在主設定視窗中顯示
顯然在這個屬性視窗中我們無法設定線路的任何資料,但從這裡可以觀察到audio line的一種直覺的表示
其它相關的參考:--------------------------------------------------------
一個混音裝置總是有一個或多個目的線路,一般來說會有兩個,分别用來輸入和輸出,
如果存在錄象裝置(Video capturing devices)會有例外,因為這些裝置為了保證最佳的影像效果,通常會隻暴露單一的音頻目的線路(audio destination)來讓使用者控制錄音的音量,這使得使用者需要安裝另一個混音裝置來提供目的線路支援播放(回放)
混音器是系統共享的資源,就像剪切闆一樣,在一個應用程式中修改設定會影響到其它使用音頻裝置資源的程式
一些聲霸卡制造商提供了相應的聲霸卡控制程式來取代系統混音控制程式,這些程式可能會功能相對更加健全,更能發揮聲霸卡的潛力,但是一般會更加複雜難以了解和使用
開始程式設計:--------------------------------------------------------------
資料結構:
MIXERLINE包含了由一條audio line的資訊,如上所述,一條audio line 可能是destinaton,或者是source
是以MIXERLINE資料結構要照顧到兩種線路,是以一個MIXERLINE結構所儲存的資訊要麼是一條源線路,要麼是一條目的線路,這一點從各個資料成員的名字也可以大緻地看出來
typedef struct {
DWORD cbStruct;
DWORD dwDestination; //目的線路索引
DWORD dwSource;
//源線路索引
DWORD dwLineID;
//這條線路的ID
DWORD fdwLine;
DWORD dwUser;
DWORD dwComponentType; //元件類型,兩種線路又分為若幹個類型
DWORD cChannels;
//聲道數
DWORD cConnections; //源線路所連接配接的目的線路數目
DWORD cControls; //線路所綁定的相應控制數目
CHAR szShortName[MIXER_SHORT_NAME_CHARS];
CHAR szName[MIXER_LONG_NAME_CHARS];
struct {
DWORD dwType;
DWORD dwDeviceID;
WORD wMid;
WORD wPid;
MMVERSION vDriverVersion;
CHAR szPname[MAXPNAMELEN];
} Target;
} MIXERLINE;
-------------------一般步驟
打開mixer裝置(獲得mixer句柄)
mixerOpen
擷取音頻線路的裝置ID(獲得ID)
mixerGetID
取得mixer裝置指定音頻線路(line)的資訊
mixerGetLineInfo
得到一個或多個與某個音源線路(line)相關的控制(control)
mixerGetLineControls
取得指定控制的屬性
mixerGetControlDetails
設定指定控制的屬性
mixerSetControlDetails
關閉mixer裝置
mixerClose
--------------------------------------------
最後在codeguru上找到了一個簡單的mixer封裝類,很實用的一段代碼,用這個就可以控制系統聲音了
在這裡轉載一下
// AlexfMixer.h: interface for the CAlexfMixer class.
// CAlexfMixer - simple mixer control wrapper
// Copyright (C) Alexander Fedorov 1999
#include<windows.h>
#include <mmsystem.h>
// Thanks to Langis Pitre
#define NO_SOURCE ((MIXERLINE_COMPONENTTYPE_SRC_LAST + 1))
class CAlexfMixer
{
protected:
HMIXER m_HMixer;
INT m_iMixerControlID;
MMRESULT mmr;
DWORD m_dwChannels;
BOOL m_bSuccess;
void ZeroAll();
public:
DWORD m_dwMixerCtlBound;
BOOL IsOk() {return m_bSuccess;};
BOOL On();
BOOL Off();
DWORD GetControlValue();
BOOL SetControlValue(DWORD dw);
CAlexfMixer(DWORD DstType, DWORD SrcType, DWORD ControlType,HWND hwnd=0);
//指定視窗句柄會使裝置在系統控制改變時發送給視窗MM_MIXM_CONTROL_CHANGE
virtual ~CAlexfMixer();
};
//--------------------------------------------------------------------
//------AlexfMixer.cpp
#include"AlexfMixer.h"
void CAlexfMixer::ZeroAll()
{
m_HMixer = NULL;
m_iMixerControlID = 0;
mmr = MMSYSERR_NOERROR;
m_dwChannels = 0;
m_bSuccess = FALSE;
m_dwMixerCtlBound=0;
}
CAlexfMixer::CAlexfMixer( DWORD DstType, DWORD SrcType, DWORD ControlType,HWND hwnd)
{
ZeroAll();
if(mixerGetNumDevs()<1) return;
if(hwnd==0)
mmr = mixerOpen(&m_HMixer, 0, 0, 0L, MIXER_OBJECTF_MIXER);
else
mmr = mixerOpen(&m_HMixer, 0, (DWORD)hwnd, 0L, CALLBACK_WINDOW);
if (mmr != MMSYSERR_NOERROR) return;
// get dwLineID
MIXERLINE mxl;
mxl.cbStruct = sizeof(MIXERLINE);
// DstType
mxl.dwComponentType = DstType;
if (mixerGetLineInfo((HMIXEROBJ)m_HMixer,&mxl,MIXER_GETLINEINFOF_COMPONENTTYPE)!= MMSYSERR_NOERROR)
return;
// SrcType
if( SrcType != NO_SOURCE )
{
UINT nconn = mxl.cConnections;
DWORD DstIndex = mxl.dwDestination;
for( UINT j = 0; j < nconn; j++ )
{
mxl.cbStruct = sizeof( MIXERLINE );
mxl.dwSource = j;
mxl.dwDestination = DstIndex;
if(mixerGetLineInfo( ( HMIXEROBJ )m_HMixer,
&mxl, MIXER_GETLINEINFOF_SOURCE ) != MMSYSERR_NOERROR) return;
//檢測出相應的線路即退出循環,進而在mxl中保留線路資訊
if( mxl.dwComponentType == SrcType ) break;
}
}
// get dwControlID
MIXERCONTROL mxc;
MIXERLINECONTROLS mxlc;
mxlc.cbStruct = sizeof(MIXERLINECONTROLS);
mxlc.dwLineID = mxl.dwLineID;
mxlc.dwControlType = ControlType;
mxlc.cControls = 1;
mxlc.cbmxctrl = sizeof(MIXERCONTROL);
mxlc.pamxctrl = &mxc;
if (mixerGetLineControls((HMIXEROBJ)m_HMixer, &mxlc,MIXER_GETLINECONTROLSF_ONEBYTYPE)!=MMSYSERR_NOERROR)
return;
m_iMixerControlID = mxc.dwControlID;
m_dwMixerCtlBound=mxc.Bounds.dwMaximum-mxc.Bounds.dwMinimum;
m_dwChannels = mxl.cChannels;
if (m_dwChannels > 0) m_dwChannels--;
m_bSuccess = TRUE;
}
CAlexfMixer::~CAlexfMixer()
{
if (m_HMixer) mixerClose(m_HMixer);
}
DWORD CAlexfMixer::GetControlValue()
{
if (!m_bSuccess) return 0;
m_bSuccess = FALSE;
MIXERCONTROLDETAILS mxcd;
MIXERCONTROLDETAILS_UNSIGNED mxcd_u;
mxcd.cbStruct = sizeof(mxcd);
//根據controlID來擷取control的詳細資訊
mxcd.dwControlID = m_iMixerControlID;
mxcd.cChannels = m_dwChannels;
mxcd.cMultipleItems = 0;
mxcd.cbDetails = sizeof(mxcd_u);
mxcd.paDetails = &mxcd_u;
mmr = mixerGetControlDetails((HMIXEROBJ)m_HMixer, &mxcd, 0L);
if (MMSYSERR_NOERROR != mmr) return 0;
m_bSuccess = TRUE;
return mxcd_u.dwValue;
}
BOOL CAlexfMixer::SetControlValue(DWORD dw)
{
if (!m_bSuccess) return m_bSuccess;
m_bSuccess = FALSE;
MIXERCONTROLDETAILS mxcd;
MIXERCONTROLDETAILS_UNSIGNED mxcd_u;
mxcd.cbStruct = sizeof(mxcd);
mxcd.dwControlID = m_iMixerControlID;
mxcd.cChannels = m_dwChannels;
mxcd.cMultipleItems = 0;
mxcd.cbDetails = sizeof(mxcd_u);
mxcd.paDetails = &mxcd_u;
mmr = mixerGetControlDetails((HMIXEROBJ)m_HMixer, &mxcd, 0L);
if (MMSYSERR_NOERROR != mmr) return m_bSuccess;
mxcd_u.dwValue = dw;
mmr = mixerSetControlDetails((HMIXEROBJ)m_HMixer, &mxcd, 0L);
if (MMSYSERR_NOERROR != mmr) return m_bSuccess;
m_bSuccess = TRUE;
return m_bSuccess;
}
BOOL CAlexfMixer::On()
{
return SetControlValue(0);
}
BOOL CAlexfMixer::Off()
{
return SetControlValue(1);
}
//---------------------------------------------------------------------------------
//如何使用
//main.cpp--------------
// cl /c /GX main.cpp AlexfMxier.cpp
//link main.obj AlexfMixer.obj winmm.lib
#include<iostream>
#include"alexfmixer.h"
using namespace std;
void main(){
//初始化為設定聲音
CAlexfMixer mixer(MIXERLINE_COMPONENTTYPE_DST_SPEAKERS,NO_SOURCE,
MIXERCONTROL_CONTROLTYPE_VOLUME);
//檢查是否出錯
if (!mixer.IsOk()){
MessageBox(NULL,TEXT("打開裝置過程出錯!"),NULL,MB_OK);
return;
}
cout<<"系統音量範圍"<<mixer.m_dwMixerCtlBound<<endl;
cout<<"目前系統音量"<<mixer.GetControlValue()<<endl;
cout<<"将系統音量設定為40000"<<endl;
mixer.SetControlValue(40000);
}
這個類是在codeguru上找到的,在這裡說一下自己的重要體會:
學做程式很重要的一點,就是學會尋找自己需要的資源拿來學習并加以複用,很高興認識的codeguru和codeproject,上面真的有很多現成的好的例子,以前自己幹了太多浪費時間的事情,很多時候都需要學會更好的交流方法,不能自己死腦筋recreate the wheel(中國有一句叫閉門造車一個意思)
//END