{===============================================================} { } { Ms Mixer API interface unit } { a code by Lake Unanimated / unanimated@geocities.com } { } { } { Copyright (c) 2000 Lake of Soft, Ltd } { All Rights Reserved } { } {===============================================================} {$B-} unit MsMixerThorax; interface uses Windows, Classes, Contnrs, MMSystem; {$IFDEF VER110} type LongWord = Cardinal; {$ENDIF} type tMsMixer = class; tMsMixerSystem = class; tMsMixerLine = class; tMsMixerControl = class private fMaster : tMsMixerLine; fCaps : MixerControl; fDetails: tMixerControlDetails; fNeedUpdateDetails: Boolean; fLastError : MMResult; fListTextItems: tStrings; fUpdateCount : Integer; fWndHandle : tHandle; function GetControlCaps: pMixerControl; function GetMixerControlDetails: pMixerControlDetails; function GetIsCustom: Boolean; function GetIsUniform: Boolean; function GetIsMultiple: Boolean; function GetBoolDetails(aChannel: Byte; aIndex: Cardinal): Boolean; function GetSignedDetails(aChannel: Byte; aIndex: Cardinal): LongInt; function GetTextDetails(aChannel: Byte; aIndex: Cardinal): string; function GetUnsignedDetails(aChannel: Byte; aIndex: Cardinal): LongWord; procedure SetBoolDetails(aChannel: Byte; aIndex: Cardinal; Value: Boolean); procedure SetSignedDetails(aChannel: Byte; aIndex: Cardinal; Value: LongInt); procedure SetUnsignedDetails(aChannel: Byte; aIndex: Cardinal; Value: LongWord); procedure SetDetails; function GetIsPMControl: Boolean; public constructor Create(aMaster: tMsMixerLine; aCaps: pMixerControl); destructor Destroy; override; procedure BeginUpdate; procedure EndUpdate; // property NeedUpdateDetails : Boolean read fNeedUpdateDetails write fNeedUpdateDetails; property LastError : MMResult read fLastError; property OwnerHandle : tHandle read fWndHandle write fWndHandle; property ControlCaps : pMixerControl read GetControlCaps; property ControlDetails : pMixerControlDetails read GetMixerControlDetails; property IsCustomControl : Boolean read GetIsCustom; property IsUniformControl : Boolean read GetIsUniform; property IsMultipleControl : Boolean read GetIsMultiple; property IsPeakMeterControl: Boolean read GetIsPMControl; property ListTextItems : tStrings read fListTextItems; property Details_Boolean [aChannel: Byte; aIndex: Cardinal]: Boolean read GetBoolDetails write SetBoolDetails; property Details_Signed [aChannel: Byte; aIndex: Cardinal]: LongInt read GetSignedDetails write SetSignedDetails; property Details_Unsigned[aChannel: Byte; aIndex: Cardinal]: LongWord read GetUnsignedDetails write SetUnsignedDetails; property Details_Text [aChannel: Byte; aIndex: Cardinal]: string read GetTextDetails; end; tMsMixerLine = class private fMaster: tMsMixer; fCaps : MixerLine; fNeedUpdateCaps: Boolean; fLastError : MMResult; fIsSourceLine : Boolean; fSourceLines : tObjectList; fControls : tObjectList; function GetLineCaps: pMixerLine; function GetSourceLineByIndex(aIndex: Cardinal): tMsMixerLine; function GetSourceLineCount: Cardinal; function GetControlByIndex(aIndex: Cardinal): tMsMixerControl; function GetControlCount: Cardinal; public constructor Create(aMaster: tMsMixer; aIndex: Cardinal; aIsSourceLine: Boolean = True); destructor Destroy; override; procedure EnumSourceLines; procedure EnumControls; // property LastError: MMResult read fLastError; property LineCaps: pMixerLine read GetLineCaps; property IsSourceLine: Boolean read fIsSourceLine; property SourceLineCount: Cardinal read GetSourceLineCount; property SourceLineByIndex[aIndex: Cardinal]: tMsMixerLine read GetSourceLineByIndex; default; property ControlCount: Cardinal read GetControlCount; property ControlByIndex[aIndex: Cardinal]: tMsMixerControl read GetControlByIndex; end; tMsMixer = class private fMaster: tMsMixerSystem; fIndex : Cardinal; fHandle: hMixer; fLastError: MMResult; fCaps : MixerCaps; fNeedUpdateCaps: Boolean; fLines : tObjectList; fWinHandle: tHandle; function GetActive: Boolean; procedure SetActive(Value: Boolean); function GetCaps: pMixerCaps; function GetMixerID: Cardinal; function GetCount: Cardinal; function GetMsLineByIndex(aIndex: Cardinal): tMsMixerLine; protected procedure DoOpen; virtual; procedure DoClose; virtual; public constructor Create(aMaster: tMsMixerSystem; aIndex: Cardinal); destructor Destroy; override; procedure Open; procedure Close; procedure EnumLines; // property LastError: MMResult read fLastError; property Active : Boolean read GetActive write SetActive; property MixerCaps: pMixerCaps read GetCaps; property MixerID : Cardinal read GetMixerID; property LineByIndex[aIndex: Cardinal]: tMsMixerLine read GetMsLineByIndex; default; property LineCount: Cardinal read GetCount; property WinHandle: tHandle read fWinHandle write fWinHandle; end; tMsMixerSystem = class(tComponent) private fMixers: tObjectList; function GetCount: Cardinal; function GetMsMixerbyIndex(aIndex: Cardinal): tMsMixer; protected procedure EnumMixers; virtual; public constructor Create(aOwner: tComponent); override; destructor Destroy; override; // property MixerByIndex[aIndex: Cardinal]: tMsMixer read GetMsMixerByIndex; default; property MixerCount: Cardinal read GetCount; end; implementation uses SysUtils; { tMsMixerControl } procedure tMsMixerControl.BeginUpdate; begin Inc(fUpdateCount); end; constructor tMsMixerControl.Create(aMaster: tMsMixerLine; aCaps: pMixerControl); begin inherited Create; fMaster := aMaster; fCaps := aCaps^; fNeedUpdateDetails := True; fListTextItems := tStringList.Create; fWndHandle := fMaster.fMaster.WinHandle; FillChar(fDetails, SizeOf(fDetails), #0); fDetails.cbStruct := SizeOf(fDetails); end; destructor tMsMixerControl.Destroy; begin ReallocMem(fDetails.paDetails, 0); fListTextItems.Free; inherited; end; type pBoolArray = ^tBoolArray; tBoolArray = array[0..0] of MIXERCONTROLDETAILS_BOOLEAN; pSignedArray = ^tSignedArray; tSignedArray = array[0..0] of MIXERCONTROLDETAILS_SIGNED; pUnSignedArray = ^tUnSignedArray; tUnSignedArray = array[0..0] of MIXERCONTROLDETAILS_UNSIGNED; procedure tMsMixerControl.EndUpdate; begin Dec(fUpdateCount); if (fUpdateCount = 0) then SetDetails; if (fUpdateCount < 0) then fUpdateCount := 0; end; function tMsMixerControl.GetBoolDetails(aChannel: Byte; aIndex: Cardinal): Boolean; begin if IsMultipleControl then Result := LongBool(pBoolArray(ControlDetails.paDetails)[aChannel * fCaps.cMultipleItems + aIndex].fValue) else Result := LongBool(pBoolArray(ControlDetails.paDetails)[aChannel].fValue); end; function tMsMixerControl.GetControlCaps: pMixerControl; begin Result := @fCaps; end; function tMsMixerControl.GetIsCustom: Boolean; begin Result := (fCaps.dwControlType = MIXERCONTROL_CONTROLTYPE_CUSTOM); end; function tMsMixerControl.GetIsMultiple: Boolean; begin Result := ((fCaps.fdwControl and MIXERCONTROL_CONTROLF_MULTIPLE) <> 0); end; function tMsMixerControl.GetIsPMControl: Boolean; begin Result := (ControlCaps.dwControlType = MIXERCONTROL_CONTROLTYPE_BOOLEANMETER) or (ControlCaps.dwControlType = MIXERCONTROL_CONTROLTYPE_SIGNEDMETER) or (ControlCaps.dwControlType = MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER) or (ControlCaps.dwControlType = MIXERCONTROL_CONTROLTYPE_PEAKMETER); end; function tMsMixerControl.GetIsUniform: Boolean; begin Result := ((fCaps.fdwControl and MIXERCONTROL_CONTROLF_UNIFORM) <> 0); end; function tMsMixerControl.GetMixerControlDetails: pMixerControlDetails; var Z: Cardinal; i: Integer; O: Cardinal; begin if fNeedUpdateDetails then with fDetails do begin dwControlID := fCaps.dwControlID; if IsCustomControl then cChannels := 0 else if IsUniformControl then cChannels := 1 else cChannels := fMaster.LineCaps.cChannels; // apply at all channels if IsMultipleControl then cMultipleItems := fCaps.cMultipleItems else if IsCustomControl then hwndOwner := OwnerHandle; if (fCaps.dwControlType = MIXERCONTROL_CONTROLTYPE_EQUALIZER) or (fCaps.dwControlType = MIXERCONTROL_CONTROLTYPE_MIXER) or (fCaps.dwControlType = MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT) or (fCaps.dwControlType = MIXERCONTROL_CONTROLTYPE_MUX) or (fCaps.dwControlType = MIXERCONTROL_CONTROLTYPE_SINGLESELECT) then begin cbDetails := SizeOf(MIXERCONTROLDETAILS_LISTTEXT); if IsMultipleControl then Z := cChannels * cMultipleItems * cbDetails else Z := cChannels * cbDetails; ReallocMem(paDetails, Z); fListTextItems.Clear; fLastError := mixerGetControlDetails(fMaster.fMaster.fIndex, @fDetails, MIXER_GETCONTROLDETAILSF_LISTTEXT + MIXER_OBJECTF_MIXER); if (fLastError = MMSYSERR_NOERROR) then begin O := 0; if IsMultipleControl then Z := cChannels * cMultipleItems else Z := cChannels; for i := 0 to Z - 1 do begin fListTextItems.Add(pMIXERCONTROLDETAILSLISTTEXT(@pChar(fDetails.paDetails)[O]).szName); Inc(O, cbDetails); end; end; end; if IsCustomControl then cbDetails := fCaps.Metrics.cbCustomData else case fCaps.dwControlType of // boolean MIXERCONTROL_CONTROLTYPE_BOOLEANMETER, MIXERCONTROL_CONTROLTYPE_BOOLEAN, MIXERCONTROL_CONTROLTYPE_BUTTON, MIXERCONTROL_CONTROLTYPE_LOUDNESS, MIXERCONTROL_CONTROLTYPE_MONO, MIXERCONTROL_CONTROLTYPE_MUTE, MIXERCONTROL_CONTROLTYPE_ONOFF, MIXERCONTROL_CONTROLTYPE_STEREOENH, MIXERCONTROL_CONTROLTYPE_MIXER, MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT, MIXERCONTROL_CONTROLTYPE_MUX, MIXERCONTROL_CONTROLTYPE_SINGLESELECT : cbDetails := SizeOf(MIXERCONTROLDETAILS_BOOLEAN); // listtext {MIXERCONTROL_CONTROLTYPE_EQUALIZER, MIXERCONTROL_CONTROLTYPE_MIXER, MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT, MIXERCONTROL_CONTROLTYPE_MUX, MIXERCONTROL_CONTROLTYPE_SINGLESELECT : cbDetails := SizeOf(MIXERCONTROLDETAILS_LISTTEXT);} // signed MIXERCONTROL_CONTROLTYPE_PEAKMETER, MIXERCONTROL_CONTROLTYPE_SIGNEDMETER, MIXERCONTROL_CONTROLTYPE_SIGNED, MIXERCONTROL_CONTROLTYPE_DECIBELS, MIXERCONTROL_CONTROLTYPE_PAN, MIXERCONTROL_CONTROLTYPE_QSOUNDPAN, MIXERCONTROL_CONTROLTYPE_SLIDER : cbDetails := SizeOf(MIXERCONTROLDETAILS_SIGNED); // unsigned MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER, MIXERCONTROL_CONTROLTYPE_UNSIGNED, MIXERCONTROL_CONTROLTYPE_BASS, MIXERCONTROL_CONTROLTYPE_EQUALIZER, MIXERCONTROL_CONTROLTYPE_FADER, MIXERCONTROL_CONTROLTYPE_TREBLE, MIXERCONTROL_CONTROLTYPE_VOLUME, MIXERCONTROL_CONTROLTYPE_MICROTIME, MIXERCONTROL_CONTROLTYPE_MILLITIME, MIXERCONTROL_CONTROLTYPE_PERCENT : cbDetails := SizeOf(MIXERCONTROLDETAILS_UNSIGNED); else cbDetails := 0; end; if IsMultipleControl then Z := cChannels * cMultipleItems * cbDetails else Z := cChannels * cbDetails; ReallocMem(paDetails, Z); fLastError := mixerGetControlDetails(fMaster.fMaster.fIndex, @fDetails, MIXER_GETCONTROLDETAILSF_VALUE + MIXER_OBJECTF_MIXER); fNeedUpdateDetails := (fLastError <> MMSYSERR_NOERROR); end; Result := @fDetails; end; function tMsMixerControl.GetSignedDetails(aChannel: Byte; aIndex: Cardinal): LongInt; begin if IsMultipleControl then Result := pSignedArray(ControlDetails.paDetails)[aChannel * fCaps.cMultipleItems + aIndex].lValue else Result := pSignedArray(ControlDetails.paDetails)[aChannel].lValue; end; function tMsMixerControl.GetTextDetails(aChannel: Byte; aIndex: Cardinal): string; begin if IsMultipleControl then Result := fListTextItems[aChannel * fCaps.cMultipleItems + aIndex] else Result := fListTextItems[aChannel]; end; function tMsMixerControl.GetUnsignedDetails(aChannel: Byte; aIndex: Cardinal): LongWord; begin if IsMultipleControl then Result := pUnSignedArray(ControlDetails.paDetails)[aChannel * fCaps.cMultipleItems + aIndex].dwValue else Result := pUnSignedArray(ControlDetails.paDetails)[aChannel].dwValue; end; procedure tMsMixerControl.SetBoolDetails(aChannel: Byte; aIndex: Cardinal; Value: Boolean); begin GetMixerControlDetails; if IsMultipleControl then LongBool(pBoolArray(fDetails.paDetails)[aChannel * fCaps.cMultipleItems + aIndex].fValue) := Value else LongBool(pBoolArray(fDetails.paDetails)[aChannel].fValue) := Value; SetDetails; end; procedure tMsMixerControl.SetDetails; begin if (fUpdateCount = 0) then fLastError := mixerSetControlDetails(fMaster.fMaster.fIndex, @fDetails, MIXER_SETCONTROLDETAILSF_VALUE + MIXER_OBJECTF_MIXER); end; procedure tMsMixerControl.SetSignedDetails(aChannel: Byte; aIndex: Cardinal; Value: LongInt); begin GetMixerControlDetails; if IsMultipleControl then pSignedArray(fDetails.paDetails)[aChannel * fCaps.cMultipleItems + aIndex].lValue := Value else pSignedArray(fDetails.paDetails)[aChannel].lValue := Value; SetDetails; end; procedure tMsMixerControl.SetUnsignedDetails(aChannel: Byte; aIndex: Cardinal; Value: LongWord); begin GetMixerControlDetails; if IsMultipleControl then pUnSignedArray(fDetails.paDetails)[aChannel * fCaps.cMultipleItems + aIndex].dwValue := Value else pUnSignedArray(fDetails.paDetails)[aChannel].dwValue := Value; SetDetails; end; { tMsMixerLine } constructor tMsMixerLine.Create(aMaster: tMsMixer; aIndex: Cardinal; aIsSourceLine: Boolean{ = True}); begin inherited Create; fMaster := aMaster; fIsSourceLine := aIsSourceLine; if not IsSourceLine then fSourceLines := tObjectList.Create; fControls := tObjectList.Create; fNeedUpdateCaps := True; FillChar(fCaps, SizeOf(fCaps), #0); fCaps.cbStruct := SizeOf(fCaps); fCaps.dwDestination := aIndex; end; destructor tMsMixerLine.Destroy; begin fSourceLines.Free; fControls.Free; inherited; end; procedure tMsMixerLine.EnumControls; var i: Cardinal; C: Cardinal; Details: MixerLineControls; O: Cardinal; D: Cardinal; begin C := LineCaps.cControls; fControls.Clear; FillChar(Details, SizeOf(Details), #0); if (C > 0) then with Details do begin cbStruct := SizeOf(Details); dwLineID := LineCaps.dwLineID; cControls := LineCaps.cControls; D := SizeOf(MIXERCONTROL); cbmxctrl := D; pamxctrl := AllocMem(cbmxctrl * cControls); try fLastError := mixerGetLineControls(fMaster.fIndex, @Details, MIXER_GETLINECONTROLSF_ALL + MIXER_OBJECTF_MIXER); if (fLastError = MMSYSERR_NOERROR) then begin O := 0; for i := 0 to cControls - 1 do begin fControls.Add(tMsMixerControl.Create(self, @pChar(pamxctrl)[O])); Inc(O, D); end; end; finally ReallocMem(pamxctrl, 0); end; end; end; procedure tMsMixerLine.EnumSourceLines; var C: Cardinal; L: tMsMixerLine; begin if not IsSourceLine then begin fSourceLines.Clear; C := LineCaps.cConnections; while (C > 0) do begin Dec(C); L := tMsMixerLine.Create(fMaster, fCaps.dwDestination); L.fCaps.dwSource := C; fLastError := mixerGetLineInfo(fMaster.fIndex, @L.fCaps, MIXER_GETLINEINFOF_SOURCE + MIXER_OBJECTF_MIXER); L.fNeedUpdateCaps := (fLastError <> MMSYSERR_NOERROR); fSourceLines.Add(L); end; end; end; function tMsMixerLine.GetControlByIndex(aIndex: Cardinal): tMsMixerControl; begin if (aIndex < ControlCount) then Result := tMsMixerControl(fControls.Items[aIndex]) else Result := nil; end; function tMsMixerLine.GetControlCount: Cardinal; begin Result := fControls.Count; end; function tMsMixerLine.GetLineCaps: pMixerLine; begin if fNeedUpdateCaps then begin fLastError := mixerGetLineInfo(fMaster.fIndex, @fCaps, MIXER_GETLINEINFOF_DESTINATION + MIXER_OBJECTF_MIXER); fNeedUpdateCaps := (fLastError <> MMSYSERR_NOERROR); end; Result := @fCaps; end; function tMsMixerLine.GetSourceLineByIndex(aIndex: Cardinal): tMsMixerLine; begin if IsSourceLine then Result := nil else if (aIndex < SourceLineCount) then Result := tMsMixerLine(fSourceLines[aIndex]) else Result := nil; end; function tMsMixerLine.GetSourceLineCount: Cardinal; begin if IsSourceLine then Result := 0 else Result := fSourceLines.Count; end; { tMsMixer } procedure tMsMixer.Close; begin Active := False; end; constructor tMsMixer.Create(aMaster: tMsMixerSystem; aIndex: Cardinal); begin inherited Create; fMaster := aMaster; fLines := tObjectList.Create; fNeedUpdateCaps := True; FillChar(fCaps, SizeOf(fCaps), #0); //Open; end; destructor tMsMixer.Destroy; begin Close; fLines.Free; inherited; end; procedure tMsMixer.DoClose; begin fLastError := mixerClose(fHandle); fHandle := 0; end; procedure tMsMixer.DoOpen; begin if (fWinHandle = 0) then fLastError := mixerOpen(@fHandle, fIndex, 0, 0, MIXER_OBJECTF_MIXER) else fLastError := mixerOpen(@fHandle, fIndex, fWinHandle, Cardinal(Self), CALLBACK_WINDOW + MIXER_OBJECTF_MIXER); fNeedUpdateCaps := Active; end; procedure tMsMixer.EnumLines; var i: Cardinal; begin fLines.Clear; for i := 0 to MixerCaps.cDestinations - 1 do fLines.Add(tMsMixerLine.Create(Self, i, False)); end; function tMsMixer.GetActive: Boolean; begin Result := (fHandle <> 0); end; function tMsMixer.GetCaps: pMixerCaps; begin if fNeedUpdateCaps then begin fLastError := mixerGetDevCaps(fHandle, @fCaps, SizeOf(fCaps)); fNeedUpdateCaps := (fLastError <> MMSYSERR_NOERROR); end; Result := @fCaps; end; function tMsMixer.GetCount: Cardinal; begin Result := fLines.Count; end; function tMsMixer.GetMixerID: Cardinal; {$IFDEF VER110} var lR: Integer; begin fLastError := mixerGetID(fIndex, lR, MIXER_OBJECTF_MIXER); Result := lR; {$ELSE} begin fLastError := mixerGetID(fIndex, Result, MIXER_OBJECTF_MIXER); {$ENDIF} end; function tMsMixer.GetMsLineByIndex(aIndex: Cardinal): tMsMixerLine; begin if (aIndex < LineCount) then Result := tMsMixerLine(fLines[aIndex]) else Result := nil; end; procedure tMsMixer.Open; begin Active := True; end; procedure tMsMixer.SetActive(Value: Boolean); begin if (Active <> Value) then if Value then DoOpen else DoClose; end; { tMsMixerSystem } constructor tMsMixerSystem.Create(aOwner: tComponent); begin inherited; fMixers := tObjectList.Create; EnumMixers; end; destructor tMsMixerSystem.Destroy; begin fMixers.Free; inherited; end; procedure tMsMixerSystem.EnumMixers; var C: Cardinal; begin C := mixerGetNumDevs; fMixers.Clear; while (C > 0) do begin fMixers.Add(tMsMixer.Create(Self, C)); Dec(C); end; end; function tMsMixerSystem.GetCount: Cardinal; begin Result := fMixers.Count; end; function tMsMixerSystem.GetMsMixerByIndex(aIndex: Cardinal): tMsMixer; begin if (aIndex < MixerCount) then Result := tMsMixer(fMixers[aIndex]) else Result := nil; end; end.