详细的解答:
//谢谢了。
为了便于大家以后查询,将原文贴于以下
Delphi 4下编写Audio Mixer Control
Windows95下的 Volume Control(音量控制器)是一个很常用的小程序。但控制器太多摆不
下,而且回放和录音之间要经常切换,很不方便。一些朋友编写多媒体播放器,是否想过加
个内置混音器呢?你有没有想过自己编写混音器程序呢?下面具体的编写方法。
一、混音器的结构
首先简单介绍混音器的结构。混音器由多个destination(目的单元)组成,如Playback(回
放)、Recording(录音)、Voice command(语音命令)等等。 而每个destination又由多个Con
nections(连接设备), 如回放下有CD Audio、MIDI 、Wave等等。每条 Connection 联系着
一个或以上的Control(控制器)。Control是混音器的关键,有Volume Control(音量控制器)、
Mute Control(静音控制器)、Meter Control(仪表控制器)等等。
以下是Mixer 的结构及调用的函数
Mixer (mixerGetNumDevs返回mixer 的个数, 后要用Mixeropen打开混音器)
|
Destinations (destinations should be for example: Playback, Recording and Voic
e commands)
* | (The number of destinations return by mixerGetDevCaps)
* |--Destination[0] (调用 mixerGetLineInfo 取得Destination连接的Connections和Controls)
* | |
* | |--Controls (controls of the line, ex: Master volume, master mute)
* | | | (调用MixerGetLineControls 取得 Controls)
* | | |--Control[0]
* | | |--Control[1]
* | | |--Control[..]
* | |
* | |--Connections (ex: Wave, MIDI, CD Audio, Line-In,...)
* | |
* | |--Connection[0]
* | | |--Controls (here can be volume and mute)
* | | | (调用MixerGetLineControls 取得 Controls)
* | | |--Control[0] (ex:Wave Volume)
* | | |--Control[1] (ex:Wave Mute)
* | | |--Control[..] (ex:Wave Meter)
* | |
* | |--Connection[1]
* | |--Connection[..]
* |
* |--Destination[1]
* |--Destination[..]
一步一步的先后取得Destinations、Connections,但最终的目的是取得各种的Controls,
以后的音量、静音的控制都要调用Controls。所以要保存Controls的数据。本程序用一
个三维的动态数组保存Controls。方便以后调用。
二、本程序的函数的作用
getvolume 取得音量, setvolume设置音量;
getmute取得设备是否静音, setmute是设备静音。
Getpeak取得设备的peak(振幅)
五个程序都是填写Tmixercontroldetails类型调用 mixergetcontroldetails,mixerse
tcontroldetails。
三、关于mixer 的message及Callback Function
Mixer 提供
MM_MIXM_CONTROL_CHANGE
MM_MIXM_LINE_CHANGE
两个message要在调用 mixeropen 设置Callback Function
unit Unit2;
//Written by David Jiang(江天送)
//july 25th. 1999
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs,StdCtrls, ComCtrls, mmsystem, Buttons, ExtCtrls;
type Tvolume=record
left,right:word;
end;
type
TForm2 = class(TForm)
Lines: TComboBox;
Controls: TComboBox;
Label1: TLabel;
lmute: TLabel;
lmeter: TLabel;
LVolume: TTrackBar;
Rvolume: TTrackBar;
cmute: TCheckBox;
CheckBox1: TCheckBox;
About: TSpeedButton;
BExit: TSpeedButton;
RProgressBar: TProgressBar;
Timer1: TTimer;
procedure FormCreate(Sender: TObject);
procedure getlinecontrol;
procedure LinesChange(Sender: TObject);
procedure ControlsChange(Sender: TObject);
procedure LVolumeChange(Sender: TObject);
procedure cmuteClick(Sender: TObject);
procedure RvolumeChange(Sender: TObject);
procedure AboutClick(Sender: TObject);
procedure BExitClick(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure FormClose(Sender: TObject;
var
Action: TCloseAction);
private
{ Private declarations }
procedure mmixer(var msg:Tmessage);
public
{ Public declarations }
end;
function getvolume(control
mixercontrol;var
volume:Tvolume):boolean;
function setvolume(control
mixercontrol;
volume:Tvolume):boolean;
function setmute(control
mixercontrol;
mute:integer):boolean;
function getmute(control
mixercontrol;var
mute:integer):boolean;
function getpeak(control
mixercontrol;var
peak:integer):boolean;
procedure Fillstruct(control
mixercontrol;var
Cdetails:Tmixercontroldetails);
var
Form2: TForm2;
fmixerhandle:hmixer;
//混音器的句柄
mcontrols:array of array of array of PMixerControl;
line:array of Tstringlist;
peaknum:integer;
//meter mcontrols的位置
implementation
{$R *.DFM}
procedure Tform2.mmixer(var msg:Tmessage);
var
volume:Tvolume;
mute:integer;
begin
if msg.Msg=MM_MIXM_CONTROL_CHANGE then
begin
if mcontrolS[lines.itemindex][controls.itemindex]
[0].dwcontrolID=msg.LParam then
if getvolume(mcontrolS[lines.itemindex]
[controls.itemindex][0],volume) then
begin
Rvolume.Position :=-volume.right;
Lvolume.Position :=-volume.left;
end;
if mcontrolS[lines.itemindex][controls.itemindex]
[1].dwcontrolid=msg.LParam then
if getmute(mcontrolS[lines.itemindex]
[controls.itemindex][1],mute) then
cmute.Checked:=mute=1;
end;
end;
procedure TForm2.FormCreate(Sender: TObject);
begin
getlinecontrol;
lines.ItemIndex :=0;
controls.Items:=line[lines.ItemIndex];
controls.ItemIndex :=0;
controls.OnChange (sender);
end;
procedure Tform2.getlinecontrol;
var
mixerID:integer;
//混音器的ID
FMixerCaps:TMixerCaps;
n,j,k,i:integer;
ml,ml2:TMIXERLINE;
MLC:TMixerLineControls;
P
MixerControl;
begin
if mixerGetNumDevs=0 then
exit;
//如没有混合器装置,退出
fmixerhandle:=0;
mixerID:=0;
mixerOpen (@FMixerHandle,mixerID,AllocateHWnd(mmixer),
0,CALLBACK_WINDOW OR MIXER_OBJECTF_MIXER);
//打开混音器
mixerGetDevCaps (mixerID,@FMixerCaps,SizeOf (TMixerCaps));
//返回混音器的兼容性
setlength(line,fmixercaps.cDestinations);
setlength(mcontrols,fmixercaps.cDestinations );
for i:=0 to fmixercaps.cDestinations-1do
begin
ml.dwDestination:=i;
ml.cbStruct :=sizeof(tMIXERLINE);
mixerGetLineInfo(fmixerhandle,@ml,
MIXER_GETLINEINFOF_DESTINATION);
//取得混音器的Destinations
line
:=tstringlist.Create
setlength(mcontrols,ml.cconnections);
n:=0;
if ml.cControls>1 then
begin
n:=1;
setlength(mcontrols,ml.cconnections+1);
MLC.cbStruct:=SizeOf(MLC);
MLC.dwLineID:=ml.dwLineID;
MLC.cControls:=ml.cControls;
MLC.cbmxctrl:=SizeOf(TMixerControl);
GetMem (P,SizeOf(TMixerControl)*ml.cControls);
MLC.pamxctrl:=P;
MixerGetLineControls(fMixerHandle,@MLC,
MIXER_GETLINECONTROLSF_ALL);
setlength(mcontrols[0],ml.cControls);
line.Add(p^.szname);
//Master Volume
For k:=0 to ml.cControls-1do
begin
mcontrols[0][k]:=p;
mcontrols[0][0].Metrics.dwReserved[k+1]:=1;
inc(p);
end;
end;
lines.Items.Add(ml.szName);
ML2.cbStruct:=SizeOf(TMixerLine);
ML2.dwDestination:=ml.dwDestination
for j:=0 to ml.cConnections -1do
begin
ML2.dwSource:=j;
MixerGetLineInfo (fmixerHandle,@ML2,
MIXER_GETLINEINFOF_SOURCE);
MLC.cbStruct:=SizeOf(MLC);
MLC.dwLineID:=ml2.dwLineID;
MLC.cControls:=ml2.cControls;
MLC.cbmxctrl:=SizeOf(TMixerControl);
GetMem (P,SizeOf(TMixerControl)*ml2.cControls);
MLC.pamxctrl:=P;
MixerGetLineControls(fMixerHandle,@MLC,
MIXER_GETLINECONTROLSF_ALL);
setlength(mcontrols[j+n],ml2.cControls);
For k:=0 to ml2.cControls-1do
begin
if p.dwControlType=MIXERCONTROL_CONTROLTYPE_VOLUME
then
line.add(p^.szName);
mcontrols[j+n][k]:=p;
mcontrols[j+n][0].Metrics.dwReserved[k+1]:=1;
inc(p);
end;
//取得混音器线路的控制器
end;
end;
end;
procedure Fillstruct(controlmixercontrol;var
Cdetails:Tmixercontroldetails);
begin
cdetails.cbStruct:=sizeof(cdetails);
cdetails.dwControlID:=control.dwControlID
cdetails.cbDetails:=sizeof(integer);
cdetails.hwndOwner :=0;
end;
function getpeak(controlmixercontrol;var
peak:integer):boolean;
var
details:TMIXERCONTROLDETAILSSIGNED;
cdetails:TMIXERCONTROLDETAILS;
begin
result:=false;
if control.dwControlType <>
MIXERCONTROL_CONTROLTYPE_PEAKMETER then
exit;
cdetails.cChannels :=1;
cdetails.paDetails:=@details;
fillstruct(control,cdetails);
result:=MIXERGETCONTROLDETAILS(fmixerhandle,
@cdetails,MIXER_GETCONTROLDETAILSF_VALUE)=0;
peak:=abs(details.LValue) div 180;
end;
function setvolume(controlmixercontrol;
volume:Tvolume):boolean;
var
details:array [0..30] of Integer;
cdetails:TMIXERCONTROLDETAILS;
begin
fillstruct(control,cdetails);
cdetails.cChannels :=2;
cdetails.paDetails:=@details;
details[0]:=volume.left;
details[1]:=volume.right;
result:=MIXERSETCONTROLDETAILS(fmixerhandle,
@cdetails,MIXER_GETCONTROLDETAILSF_VALUE)=0;
end;
function getvolume(controlmixercontrol;var
volume:Tvolume):boolean;
var
details:array [0..30] of Integer;
cdetails:TMIXERCONTROLDETAILS;
begin
fillstruct(control,cdetails);
cdetails.cChannels :=2;
cdetails.paDetails:=@details;
result:=MIXERGETCONTROLDETAILS(fmixerhandle,
@cdetails,MIXER_GETCONTROLDETAILSF_VALUE)=0;
volume.left:=details[0];
volume.right:=details[1];
end;
function setmute(controlmixercontrol;
mute:integer):boolean;
var
cdetails:Tmixercontroldetails;
details:array [0..30] of Integer;
begin
fillstruct(control,cdetails);
cdetails.cChannels :=1;
cdetails.paDetails:=@details;
details[0]:=mute;
result:=MIXERSETCONTROLDETAILS(fmixerhandle,
@cdetails,MIXER_GETCONTROLDETAILSF_VALUE)=0;
end;
function getmute(controlmixercontrol;var
mute:integer):boolean;
var
cdetails:Tmixercontroldetails;
details:array [0..30] of Integer;
begin
fillstruct(control,cdetails);
cdetails.cChannels :=1;
cdetails.cMultipleItems:=0;
cdetails.paDetails:=@details;
result:=MIXERGETCONTROLDETAILS(fmixerhandle,
@cdetails,MIXER_GETCONTROLDETAILSF_VALUE)=0;
mute:=details[0];
end;
procedure TForm2.LinesChange(Sender: TObject);
begin
controls.Items:=line[lines.ItemIndex];
if controls.ItemIndex =-1 then
controls.ItemIndex :=0;
end;
procedure TForm2.ControlsChange(Sender: TObject);
var
mute,k,j:integer;
volume:Tvolume;
begin
lmute.Visible :=false;
lmeter.Visible :=false;
k:=lines.ItemIndex;j:=controls.ItemIndex;
IF mcontrols[k][j][0].Metrics.dwReserved[2]=1 then
begin
lmute.Visible:=mcontrols[k][j][1].dwcontroltype
=MIXERCONTROL_CONTROLTYPE_MUTE;
lmeter.Visible:=mcontrols[k][j][1].dwcontroltype
=MIXERCONTROL_CONTROLTYPE_PEAKMETER;
if lmeter.Visible then
peaknum:=1;
end;
cmute.Enabled :=lmute.Visible
IF mcontrols[k][j][0].Metrics.dwReserved[3]=1 then
begin
lmeter.Visible:=mcontrols[k][j][2].dwcontroltype
=MIXERCONTROL_CONTROLTYPE_PEAKMETER;
if lmeter.Visible then
peaknum:=2;
end;
if getvolume(mcontrols[k][j][0],volume) then
begin
Rvolume.Position :=-volume.right;
Lvolume.Position :=-volume.left;
end;
if getmute(mcontrols[k][j][1],mute) then
cmute.Checked :=mute=1;
RProgressbar.Visible :=lmeter.Visible;;
timer1.Enabled:=lmeter.Visible;
end;
procedure TForm2.LVolumeChange(Sender: TObject);
var volume:tvolume;
begin
if checkbox1.Checked then
Rvolume.Position :=Lvolume.Position
volume.right :=-Rvolume.Position;
volume.left:=-Lvolume.Position
setvolume(mcontrolS[lines.itemindex][controls.itemindex][0],
volume);
end;
procedure TForm2.cmuteClick(Sender: TObject);
var mute:integer;
begin
if cmute.checked then
mute:=1 else
mute:=0;
setmute(mcontrolS[lines.itemindex][controls.itemindex][1],mute);
end;
procedure TForm2.RvolumeChange(Sender: TObject);
begin
if checkbox1.Checked then
Lvolume.Position :=Rvolume.Position
Lvolume.OnChange (sender);
end;
procedure TForm2.AboutClick(Sender: TObject);
begin
showmessage('Mixer Demo version 1.0 '+chr(13)
+'Written by David Jiang');
end;
procedure TForm2.BExitClick(Sender: TObject);
begin
close;
end;
procedure TForm2.Timer1Timer(Sender: TObject);
var peak:integer;
begin
IF mcontrols[lines.itemindex][controls.itemindex]
[0].Metrics.dwReserved[peaknum+1]=1 then
if getpeak(mcontrols[lines.itemindex][controls.itemindex]
[peaknum],peak) then
Rprogressbar.Position :=peak;
end;
procedure TForm2.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
mixerclose(fmixerhandle);
end;
end.