Delphi 4 下编写Audio Mixer Control


  Windows95下的 Volume Control(音量控制器)是一个很常用的小程序。但控制器太多摆不下,而且回放和录音之间要经常切换,很不方便。一些朋友编写多媒体播放器,是否想过加个内置混音器呢?你有没有想过自己编写混音器程序呢?下面具体的编写方法。

一、混音器的结构

  首先简单介绍混音器的结构。混音器由多个destination(目的单元)组成,如Playback(回放)、Recording(录音)、Voice command(语音命令)等等。 而每个destination又由多个Connections(连接设备), 如回放下有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 Voice 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,mixersetcontroldetails。

三、关于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:Pmixercontrol;var

volume:Tvolume):boolean;

function setvolume(control:Pmixercontrol;

volume:Tvolume):boolean;

function setmute(control:Pmixercontrol;

mute:integer):boolean;

function getmute(control:Pmixercontrol;var

mute:integer):boolean;

function getpeak(control:Pmixercontrol;var

peak:integer):boolean;

procedure Fillstruct(control:Pmixercontrol;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:PMixerControl;

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-1 do

begin

ml.dwDestination:=i;

ml.cbStruct :=sizeof(tMIXERLINE);

mixerGetLineInfo(fmixerhandle,@ml,

MIXER_GETLINEINFOF_DESTINATION);

//取得混音器的Destinations

line[i]:=tstringlist.Create ;

setlength(mcontrols[i],ml.cconnections);

n:=0;

if ml.cControls>1 then

begin

n:=1;

setlength(mcontrols[i],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[i][0],ml.cControls);

line[i].Add(p^.szname); //Master Volume

For k:=0 to ml.cControls-1 do

begin

mcontrols[i][0][k]:=p;

mcontrols[i][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 -1 do

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[i][j+n],ml2.cControls);

For k:=0 to ml2.cControls-1 do

begin

if p.dwControlType=MIXERCONTROL_CONTROLTYPE_VOLUME

then line[i].add(p^.szName);

mcontrols[i][j+n][k]:=p;

mcontrols[i][j+n][0].Metrics.dwReserved[k+1]:=1;

inc(p);

end;//取得混音器线路的控制器

end;

end;

end;

procedure Fillstruct(control:Pmixercontrol;var

Cdetails:Tmixercontroldetails);

begin

cdetails.cbStruct:=sizeof(cdetails);

cdetails.dwControlID:=control.dwControlID ;

cdetails.cbDetails:=sizeof(integer);

cdetails.hwndOwner :=0;

end;

function getpeak(control:Pmixercontrol;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(control:Pmixercontrol;

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(control:Pmixercontrol;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(control:Pmixercontrol;

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(control:Pmixercontrol;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.


本站原创及翻译内容保留版权,欢迎转贴,转贴时请注明转自Delphi深度探索