MMMixer.pas
上传用户:hylc_2004
上传日期:2014-01-23
资源大小:46800k
文件大小:89k
源码类别:

Delphi控件源码

开发平台:

Delphi

  1. {========================================================================}
  2. {=                (c) 1995-98 SwiftSoft Ronald Dittrich                 =}
  3. {========================================================================}
  4. {=                          All Rights Reserved                         =}
  5. {========================================================================}
  6. {=  D 01099 Dresden             = Tel.: +0351-8012255                   =}
  7. {=  Loewenstr.7a                = info@swiftsoft.de                     =}
  8. {========================================================================}
  9. {=  Actual versions on http://www.swiftsoft.de/mmtools.html             =}
  10. {========================================================================}
  11. {=  This code is for reference purposes only and may not be copied or   =}
  12. {=  distributed in any format electronic or otherwise except one copy   =}
  13. {=  for backup purposes.                                                =}
  14. {=                                                                      =}
  15. {=  No Delphi Component Kit or Component individually or in a collection=}
  16. {=  subclassed or otherwise from the code in this unit, or associated   =}
  17. {=  .pas, .dfm, .dcu, .asm or .obj files may be sold or distributed     =}
  18. {=  without express permission from SwiftSoft.                          =}
  19. {=                                                                      =}
  20. {=  For more licence informations please refer to the associated        =}
  21. {=  HelpFile.                                                           =}
  22. {========================================================================}
  23. {=  $Date: 12.02.98 - 06:07:59 $                                        =}
  24. {========================================================================}
  25. unit MMMixer;
  26. {$I COMPILER.INC}
  27. interface
  28. uses
  29. {$IFDEF WIN32}
  30.     Windows,
  31. {$ELSE}
  32.     WinTypes,
  33.     WinProcs,
  34. {$ENDIF}
  35.     Messages,
  36.     Classes,
  37.     SysUtils,
  38.     Forms,
  39.     MMSystem,
  40.     MMObj,
  41.     MMUtils,
  42.     MMObsrv,
  43.     MMDevice;
  44. type
  45.     { D3: This lines rely on current Win32 API}
  46.     { To override Borland's bug directly declare TMixerControl }
  47.     { instead of TMixerControlA                                }
  48.     PMixerControl = ^TMixerControl;
  49.     TMixerControl = record
  50.         cbStruct     : DWORD; { size in bytes of MIXERCONTROL         }
  51.         dwControlID  : DWORD; { unique control id for mixer device    }
  52.         dwControlType: DWORD; { MIXERCONTROL_CONTROLTYPE_xxx          }
  53.         fdwControl   : DWORD; { MIXERCONTROL_CONTROLF_xxx             }
  54.         cMultipleItems: DWORD; { if MIXERCONTROL_CONTROLF_MULTIPLE set }
  55.         szShortName  : array[0..MIXER_SHORT_NAME_CHARS - 1] of AnsiChar;
  56.         szName       : array[0..MIXER_LONG_NAME_CHARS - 1] of AnsiChar;
  57.         Bounds: record
  58.            case Integer of
  59.                0: (lMinimum, lMaximum: Longint);
  60.                { !! HERE IT IS !! }
  61.                1: (dwMinimum, dwMaximum: DWORD);
  62.                2: (dwReserved: array[0..5] of DWORD);
  63.                { !! HERE IT IS !! }
  64.         end;
  65.         Metrics: record
  66.            case Integer of
  67.                0: (cSteps: DWORD);       { # of steps between min & max }
  68.                1: (cbCustomData: DWORD); { size in bytes of custom data }
  69.                2: (dwReserved: array[0..5] of DWORD);
  70.         end;
  71.     end;
  72. const
  73.     badLineId    = -1;
  74.     badControlId = -1;
  75.     NoItem       = -1;
  76. type
  77.     { D3: This lines rely on current Win32 API}
  78.     {$IFDEF WIN32}
  79.     TMMLineId           = type integer;
  80.     TMMControlId        = type integer;
  81.     TMMChannelIndex     = Integer;
  82.     TMMItemIndex        = type Integer;
  83.     TMMLineIndex        = Integer;
  84.     TMMControlIndex     = Integer;
  85.     TMMConnectionIndex  = Integer;
  86.     {$ENDIF}
  87.     TMMComponentType    = (ctDstDigital,ctDstHeadPhones,ctDstLine,
  88.                            ctDstMonitor,ctDstSpeakers,ctDstTelephone,
  89.                            ctDstUndefined,ctDstVoiceIn,ctDstWaveIn,
  90.                            ctSrcAnalog,ctSrcAux,ctSrcCD,ctSrcDigital,
  91.                            ctSrcLine,ctSrcMicrophone,ctSrcPCSpeaker,
  92.                            ctSrcSynthesizer,ctSrcTelephone,ctSrcUndefined,
  93.                            ctSrcWaveOut);
  94.     TMMControlClass     = (ccCustom,ccFader,ccList,ccMeter,ccNumber,
  95.                            ccSlider,ccSwitch,ccTime);
  96.     TMMControlClasses   = set of TMMControlClass;
  97.     TMMControlType      = (ctCustom,ctBass,ctEqualizer,ctFader,ctTreble,
  98.                            ctVolume,ctMixer,ctMultipleSelect,ctMux,
  99.                            ctSingleSelect,ctBooleanMeter,ctPeakMeter,
  100.                            ctSignerMeter,ctUnsignedMeter,ctDecibels,
  101.                            ctPercent,ctSigned,ctUnsigned,ctPan,ctQSoundPan,
  102.                            ctSlider,ctBoolean,ctButton,ctLoudness,ctMono,
  103.                            ctMute,ctOnOff,ctStereoEnh,ctMicroTime,ctMilliTime);
  104.     TMMLineChangeEvent      = procedure(Sender: TObject; LineId: TMMLineId) of object;
  105.     TMMControlChangeEvent   = procedure(Sender: TObject; LineId: TMMLineId; CtlId: TMMControlId) of object;
  106. {$IFNDEF BUILD_ACTIVEX}
  107.     TMMComponent1 = TMMComponent;
  108. {$ELSE}
  109.     TMMComponent1 = TMMNonVisualComponent;
  110. {$ENDIF}
  111.     {-- TMMMixerDevice ------------------------------------------------------}
  112.     TMMMixerDevice = class(TMMCustomAudioDevice)
  113.     private
  114.        FHandle        : HMIXER;
  115.        FCBWnd         : HWND;
  116.        FDevice        : TMMCustomAudioDevice;
  117.        FObserver      : TMMObserver;
  118.        FObservable    : TMMObservable;
  119.        FDummyInd      : TMMLineIndex;
  120.        FOnLineChange  : TMMLineChangeEvent;
  121.        FOnControlChange: TMMControlChangeEvent;
  122.        procedure SetDevice(Value: TMMCustomAudioDevice);
  123.        procedure DeviceNotify(Sender, Data: TObject);
  124.        function  StoreDeviceId: Boolean;
  125.        function  GetDestinations: TMMLineIndex;
  126.        function  GetDestination(Index: TMMLineIndex): TMMLineId;
  127.        function  GetMixerId: TMMDeviceId;
  128.        procedure MixerWndProc(var Msg: TMessage);
  129.     protected
  130.        procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  131.        procedure Changed; override;
  132.        procedure UpdateDevice; override;
  133.        procedure Open; override;
  134.        procedure Close; override;
  135.        procedure LineChanged(LineId: TMMLineId); virtual;
  136.        procedure ControlChanged(CtlId: TMMControlId); virtual;
  137.        procedure DoLineChange(LineId: TMMLineId); dynamic;
  138.        procedure DoControlChange(LineId: TMMLineId; CtlId: TMMControlId); dynamic;
  139.        { D3: This lines rely on current Win32 API}
  140.        function  GetLineInfo(var Info: TMixerLine; Flags: DWORD): Boolean;
  141.        procedure GetControlValues(Id: TMMControlId; ItemSize: DWORD; Channels: Integer; Items: TMMItemIndex; Values: Pointer; Flags: DWORD);
  142.        procedure SetControlValues(Id: TMMControlId; ItemSize: DWORD; Channels: Integer; Items: TMMItemIndex; Values: Pointer; Flags: DWORD);
  143.        function  GetControlInfo(var Info: TMixerLineControls; Flags: DWORD): Boolean;
  144.     public
  145.        constructor Create(AOwner: TComponent); override;
  146.        destructor  Destroy; override;
  147.        procedure AddObserver(O: TMMObserver);
  148.        procedure RemoveObserver(O: TMMObserver);
  149.        function  GetLineOfControl(CtlId: TMMControlId): TMMLineId;
  150.        function  GetLineInfoById(LineId: TMMLineId; var Info: TMixerLine): Boolean;
  151.        function  GetLineInfoByCompType(CompType: TMMComponentType; var Info: TMixerLine): Boolean;
  152.        function  GetLineInfoByTarget(Device: TMMCustomAudioDevice; var Info: TMixerLine): Boolean;
  153.        function  GetLineInfoByDestination(Dest: TMMLineIndex; var Info: TMixerLine): Boolean;
  154.        function  GetLineInfoBySource(Dest, Src: TMMLineIndex; var Info: TMixerLine): Boolean;
  155.        function  GetControlInfoById(ControlId: TMMControlId; var Info: TMixerControl): Boolean;
  156.        function  GetControlInfoByType(LineId: TMMLineId; ControlType: TMMControlType; var Info: TMixerControl): Boolean;
  157.        function  GetControlByType(LineId: TMMLineId; ControlType: TMMControlType): TMMControlId;
  158.        procedure GetAllControls(LineId: TMMLineId; Controls: TMMControlIndex; P: PMixerControl);
  159.        procedure GetBooleanControl(Id: TMMControlId; Channels: TMMChannelIndex; Items: TMMItemIndex; Values: PBoolean);
  160.        procedure SetBooleanControl(Id: TMMControlId; Channels: TMMChannelIndex; Items: TMMItemIndex; Values: PBoolean);
  161.        procedure GetSignedControl(Id: TMMControlId; Channels: TMMChannelIndex; Items: TMMItemIndex; Values: PInteger);
  162.        procedure SetSignedControl(Id: TMMControlId; Channels: TMMChannelIndex; Items: TMMItemIndex; Values: PInteger);
  163.        procedure GetUnsignedControl(Id: TMMControlId; Channels: TMMChannelIndex; Items: TMMItemIndex; Values: PCardinal);
  164.        procedure SetUnsignedControl(Id: TMMControlId; Channels: TMMChannelIndex; Items: TMMItemIndex; Values: PCardinal);
  165.        procedure GetItemsInfo(Id: TMMControlId; Channels: TMMChannelIndex; Items: TMMItemIndex; Infos: PMixerControlDetailsListText);
  166.        { If mixer is opened then get id of handle and update DeviceId if needed }
  167.        property MixerId: TMMDeviceId read GetMixerId;
  168.        property Destination[Index:TMMLineIndex]: TMMLineId read GetDestination;
  169.     published
  170.        property DeviceId stored StoreDeviceId;
  171.        property Device: TMMCustomAudioDevice read FDevice write SetDevice;
  172.        property Destinations: TMMLineIndex read GetDestinations write FDummyInd stored False;
  173.        property OnLineChange: TMMLineChangeEvent read FOnLineChange write FOnLineChange;
  174.        property OnControlChange: TMMControlChangeEvent read FOnControlChange write FOnControlChange;
  175.     end;
  176.     {-- TMMMixerChange --------------------------------------------------}
  177.     TMMMixerChange = class(TMMObject)
  178.     end;
  179.     {-- TMMLineChange ---------------------------------------------------}
  180.     TMMLineChange = class(TMMMixerChange)
  181.     public
  182.        LineId: TMMLineId;
  183.     end;
  184.     {-- TMMControlChange ------------------------------------------------}
  185.     TMMControlChange = class(TMMMixerChange)
  186.     public
  187.        LineId   : TMMLineId;
  188.        ControlId: TMMControlId;
  189.     end;
  190.     {-- TMMMixerDeviceError ---------------------------------------------}
  191.     EMMMixerDeviceError = class(EMMDeviceError)
  192.     end;
  193.     {-- TMMAudioLine ----------------------------------------------------}
  194.     TMMLineSetup    = (lsLineId,lsCompType,lsTarget);
  195.     { D3: This lines rely on current Win32 API}
  196.     TMMLineFlag     = (cfActive,cfDisconnected,cfSource);
  197.     TMMLineFlags    = set of TMMLineFlag;
  198.     {-- TMMLineInfo -----------------------------------------------------}
  199.     TMMLineInfo     = class(TPersistent)
  200.     private
  201.        FFlags      : TMMLineFlags;
  202.        FChannels   : Integer;
  203.        FConnections: Integer;
  204.        FControls   : Integer;
  205.        FShortName  : string;
  206.        FName       : string;
  207.        FLDummy     : Integer;
  208.        FFDummy     : TMMLineFlags;
  209.        procedure SetDummyStr(const Value: string);
  210.     public
  211.        procedure Clear;
  212.     published
  213.        property  Flags: TMMLineFlags read FFlags write FFDummy stored False;
  214.        property  Channels: TMMChannelIndex read FChannels write FLDummy stored False;
  215.        property  Connections: TMMConnectionIndex read FConnections write FLDummy stored False;
  216.        property  Controls: TMMControlIndex read FControls write FLDummy stored False;
  217.        property  ShortName: string read FShortName write SetDummyStr stored False;
  218.        property  Name: string read FName write SetDummyStr stored False;
  219.     end;
  220.     {-- TMMAudioLine ----------------------------------------------------}
  221.     TMMAudioLine = class(TMMNonVisualComponent)
  222.     private
  223.        FLineId         : TMMLineId;
  224.        FDestinationId  : TMMLineIndex;
  225.        FDestLine       : TMMAudioLine;
  226.        FDestObserver   : TMMObserver;
  227.        FLineInfo       : TMMLineInfo;
  228.        FComponentType  : TMMComponentType;
  229.        FMixer          : TMMMixerDevice;
  230.        FObserver       : TMMObserver;
  231.        FLineSetup      : TMMLineSetup;
  232.        FTarget         : TMMCustomAudioDevice;
  233.        FTargetObserver : TMMObserver;
  234.        FObservable     : TMMObservable;
  235.        FOnChange       : TNotifyEvent;
  236.        FOnControlChange: TMMControlChangeEvent;
  237.        procedure SetMixer(Value: TMMMixerDevice);
  238.        procedure MixerNotify(Sender, Data: TObject);
  239.        procedure TargetNotify(Sender, Data: TObject);
  240.        procedure DestNotify(Sender, Data: TObject);
  241.        procedure SetDestLine(Value: TMMComponent1);
  242.        function  GetDestLine : TMMComponent1;
  243.        procedure SetLineId(Value: TMMLineId);
  244.        procedure SetComponentType(Value: TMMComponentType);
  245.        procedure SetTarget(Value: TMMCustomAudioDevice);
  246.        function  StoreLineId: Boolean;
  247.        function  StoreComponentType: Boolean;
  248.        function  StoreTarget: Boolean;
  249.        procedure SetLineInfo(const Info: TMMLineInfo);
  250.        function  GetConnections: TMMConnectionIndex;
  251.        function  GetConnection(Index: TMMConnectionIndex): TMMLineId;
  252.        function  GetControls: TMMControlIndex;
  253.        function  GetControl(Index: TMMControlIndex): TMMControlId;
  254.     protected
  255.        procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  256.        procedure Changed; virtual;
  257.        procedure DoChange; dynamic;
  258.        procedure LineIdChanged; virtual;
  259.        procedure ControlChanged(CtlId: TMMControlId); virtual;
  260.        procedure DoControlChange(CtlId: TMMControlId); dynamic;
  261.        procedure UpdateLine;
  262.        procedure Loaded; override;
  263.     public
  264.        constructor Create(AOwner: TComponent); override;
  265.        destructor  Destroy; override;
  266.        procedure AddObserver(O: TMMObserver);
  267.        procedure RemoveObserver(O: TMMObserver);
  268.        function  GetLineInfoForSource(Source: TMMAudioLine; var Info : TMixerLine): Boolean;
  269.        function  ValidMixer: Boolean;
  270.        property Connections: TMMConnectionIndex read GetConnections;
  271.        property Connection[Index:TMMConnectionIndex]: TMMLineId read GetConnection;
  272.        property Controls: TMMControlIndex read GetControls;
  273.        property Control[Index:TMMControlIndex]: TMMControlId read GetControl;
  274.     published
  275.        property Mixer: TMMMixerDevice read FMixer write SetMixer;
  276.        property LineId: TMMLineId read FLineId write SetLineId stored StoreLineId default badLineId;
  277.        property ComponentType: TMMComponentType read FComponentType write SetComponentType stored StoreComponentType default Low(TMMComponentType);
  278.        property Target: TMMCustomAudioDevice read FTarget write SetTarget stored StoreTarget;
  279.        { TMMComponent because TMMAudioLine caused GPF }
  280.        property DestLine: TMMComponent1 read GetDestLine write SetDestLine;
  281.        property LineInfo: TMMLineInfo read FLineInfo write SetLineInfo stored False;
  282.        property OnChange: TNotifyEvent read FOnChange write FOnChange;
  283.        property OnControlChange: TMMControlChangeEvent read FOnControlChange write FOnControlChange;
  284.     end;
  285.     {-- TMMLineIDChange -------------------------------------------------}
  286.     TMMLineIdChange = class(TMMMixerChange)
  287.     end;
  288.     EMMAudioLineError   = class(Exception)
  289.     end;
  290.     TMMControlSetup = (csControlId, csControlType);
  291.     { D3: This lines rely on current Win32 API}
  292.     TMMControlFlag  = (cfDisabled,cfMultiple,cfUniform);
  293.     TMMControlFlags = set of TMMControlFlag;
  294.     {-- TMMControlInfo --------------------------------------------------}
  295.     TMMControlInfo  = class(TPersistent)
  296. {$IFNDEF BUILD_ACTIVEX}
  297.     private
  298. {$ELSE}
  299.     public
  300. {$ENDIF}
  301.        FFlags         : TMMControlFlags;
  302.        FMultipleItems : TMMItemIndex;
  303.        FShortName     : string;
  304.        FName          : string;
  305.        FMinValue      : Integer;
  306.        FMaxValue      : Integer;
  307.        FSteps         : Integer;
  308.        FLDummy        : Integer;
  309.        FFDummy        : TMMControlFlags;
  310.        FIDummy        : TMMItemIndex;
  311.        procedure SetDummyStr(const Value: string);
  312.     public
  313.        procedure Clear;
  314.     published
  315.        property Flags: TMMControlFlags read FFlags write FFDummy stored False;
  316.        property MultipleItems: TMMItemIndex read FMultipleItems write FIDummy stored False;
  317.        property ShortName: string read FShortName write SetDummyStr stored False;
  318.        property Name: string read FName write SetDummyStr stored False;
  319.        property MinValue: Integer read FMinValue write FLDummy stored False;
  320.        property MaxValue: Integer read FMaxValue write FLDummy stored False;
  321.        property Steps: Integer read FSteps write FLDummy stored False;
  322.     end;
  323.     { D3: This lines rely on current Win32 API}
  324.     TMMItemParam        = DWord;
  325.     TMMItemInfo         = record
  326.         Val1, Val2: TMMItemParam;
  327.         Name      : string;
  328.     end;
  329.     {-- TMMCustomMixerControl -------------------------------------------}
  330.     TMMCustomMixerControl = class(TMMNonVisualComponent)
  331.     private
  332.        FAudioLine    : TMMAudioLine;
  333.        FControlId    : TMMControlId;
  334.        FControlType  : TMMControlType;
  335.        FControlClass : TMMControlClass;
  336.        FControlSetup : TMMControlSetup;
  337.        FObserver     : TMMObserver;
  338.        FObservable   : TMMObservable;
  339.        FControlInfo  : TMMControlInfo;
  340.        FOnChange     : TNotifyEvent;
  341.        FCDummy       : TMMControlClass;
  342.        procedure SetAudioLine(Value: TMMAudioLine);
  343.        procedure SetControlId(Value: TMMControlId);
  344.        procedure SetControlType(Value: TMMControlType);
  345.        procedure LineNotify(Sender, Data: TObject);
  346.        function  GetMixer: TMMMixerDevice;
  347.        function  GetLineId: TMMLineId;
  348.        function  StoreControlId: Boolean;
  349.        procedure SetControlInfo(const Value: TMMControlInfo);
  350.        function  ValueIndex(C: TMMChannel; Item: TMMItemIndex): Integer;
  351.        function  GetItemInfo(Index: TMMItemIndex): TMMItemInfo;
  352.        function  StoreControlType: Boolean;
  353.     protected
  354.        procedure UpdateControl; virtual;
  355.        procedure Loaded; override;
  356.        procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  357.        procedure NeedMixer;
  358.        procedure NeedId;
  359.        procedure Changed; virtual;
  360.        procedure DoChange; dynamic;
  361.        procedure ControlIdChanged; virtual;
  362.        function  GetBoolean: Boolean; virtual;
  363.        procedure SetBoolean(Value: Boolean); virtual;
  364.        function  GetUnsigned: Cardinal; virtual;
  365.        procedure SetUnsigned(Value: Cardinal); virtual;
  366.        function  GetSigned: Integer; virtual;
  367.        procedure SetSigned(Value: Integer); virtual;
  368.        function  GetAvailable: Boolean; virtual;
  369.        procedure CalcParams(C: TMMChannel; Item: TMMItemIndex; var Ch: TMMChannelIndex; var It: TMMItemIndex);
  370.        function  ValidControl(const Info: TMixerControl): Boolean; virtual;
  371.        function  GetControlSetup: TMMControlSetup;
  372.        function  GetChannels: TMMChannelIndex; virtual;
  373.        function  GetItems: TMMItemIndex; virtual;
  374.     public
  375.        constructor Create(AOwner: TComponent); override;
  376.        destructor  Destroy; override;
  377.        procedure AddObserver(O: TMMObserver);
  378.        procedure RemoveObserver(O: TMMObserver);
  379.        function  GetChannelSigned(C: TMMChannel; Item: TMMItemIndex): Integer; virtual;
  380.        procedure SetChannelSigned(C: TMMChannel; Item: TMMItemIndex; Value: Integer); virtual;
  381.        function  GetChannelUnsigned(C: TMMChannel; Item: TMMItemIndex): Cardinal; virtual;
  382.        procedure SetChannelUnsigned(C: TMMChannel; Item: TMMItemIndex; Value: Cardinal); virtual;
  383.        function  GetChannelBoolean(C: TMMChannel; Item: TMMItemIndex): Boolean; virtual;
  384.        procedure SetChannelBoolean(C: TMMChannel; Item: TMMItemIndex; Value: Boolean); virtual;
  385.        function ValidMixer: Boolean;
  386.        function CanModify: Boolean;
  387.        function GetItemForLine(Line: TMMAudioLine): TMMItemIndex;
  388.        property Mixer: TMMMixerDevice read GetMixer;
  389.        property LineId: TMMLineId read GetLineId;
  390.        property AsBoolean: Boolean read GetBoolean write SetBoolean;
  391.        property AsUnsigned: Cardinal read GetUnsigned write SetUnsigned;
  392.        property AsSigned: Integer read GetSigned write SetSigned;
  393.        property Available: Boolean read GetAvailable;
  394.        property Channels: TMMChannelIndex read GetChannels;
  395.        property Items: TMMItemIndex read GetItems;
  396.        property ItemInfo[Index:TMMItemIndex]: TMMItemInfo read GetItemInfo;
  397.     published
  398.        property AudioLine: TMMAudioLine read FAudioLine write SetAudioLine;
  399.        property ControlId: TMMControlId read FControlId write SetControlId stored StoreControlId;
  400.        property ControlClass: TMMControlClass read FControlClass write FCDummy stored False;
  401.        property ControlInfo: TMMControlInfo read FControlInfo write SetControlInfo stored False;
  402.        property OnChange: TNotifyEvent read FOnChange write FOnChange;
  403.     protected
  404.        property ControlType: TMMControlType read FControlType write SetControlType stored StoreControlType;
  405.     end;
  406.     TMMControlIdChange = class(TMMMixerChange)
  407.     end;
  408.     EMMMixerControlError = class(Exception)
  409.     end;
  410.     {-- TMMMixerControl -----------------------------------------------------}
  411.     TMMMixerControl = class(TMMCustomMixerControl)
  412.     published
  413.         property    ControlType;
  414.     end;
  415. const
  416.     MaxLeftPan  = -32768;
  417.     MaxRightPan = 32767;
  418. type
  419.     {-- TMMVolumeControl ----------------------------------------------------}
  420.     TMMVolumeControl = class(TMMCustomMixerControl)
  421.     private
  422.         FPanValue: Integer;
  423.         procedure SetPanValue(Value: Integer);
  424.     protected
  425.         function  ValidControl(const Info: TMixerControl): Boolean; override;
  426.         procedure SetUnsigned(Value: Cardinal); override;
  427.         procedure SetSigned(Value: Integer); override;
  428.         function  Pan(C: TMMChannel): Extended;
  429.     public
  430.         constructor Create(AOwner: TComponent); override;
  431.         procedure SetChannelSigned(C: TMMChannel; Item: TMMItemIndex; Value: Integer); override;
  432.         procedure SetChannelUnsigned(C: TMMChannel; Item: TMMItemIndex; Value: Cardinal); override;
  433.     published
  434.         property PanValue: Integer read FPanValue write SetPanValue default 0;
  435.     end;
  436.     {-- TMMPanControl -------------------------------------------------------}
  437.     TMMPanControl = class(TMMCustomMixerControl)
  438.     private
  439.         FVolume    : TMMVolumeControl;
  440.         FObserver  : TMMObserver;
  441.         FSimulate  : Boolean;
  442.         FSimActive : Boolean;
  443.         FValue     : Integer;
  444.         procedure SetControl(Value:TMMVolumeControl);
  445.         procedure SetSimulate(Value:Boolean);
  446.         procedure VolumeNotify(Sender,Data:TObject);
  447.     protected
  448.         function  ValidControl(const Info: TMixerControl): Boolean; override;
  449.         procedure UpdatePan;
  450.         procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  451.         procedure UpdatePanValue;
  452.         procedure UpdateVolumePan;
  453.         procedure SetSigned(Value: Integer); override;
  454.         function  GetSigned: Integer; override;
  455.         function  GetAvailable: Boolean; override;
  456.         procedure UpdateControl; override;
  457.         function  GetChannels: TMMChannelIndex; override;
  458.         function  GetItems: TMMItemIndex; override;
  459.     public
  460.         constructor Create(AOwner: TComponent); override;
  461.         destructor  Destroy; override;
  462.         function  GetChannelSigned(C: TMMChannel; Item: TMMItemIndex): Integer; override;
  463.         procedure SetChannelSigned(C: TMMChannel; Item: TMMItemIndex; Value: Integer); override;
  464.     published
  465.         property  VolumeControl: TMMVolumeControl read FVolume write SetControl;
  466.         property  Simulate: Boolean read FSimulate write SetSimulate default True;
  467.     end;
  468. {-- Mixer service -------------------------------------------------------}
  469. { D3: This lines rely on current Win32 API}
  470. function APIToCompType(dwType: DWORD): TMMComponentType;
  471. function CompTypeToAPI(CompType: TMMComponentType): DWORD;
  472. function DeviceTypeToTarget(DevType: TMMAudioDeviceType): UINT;
  473. function ControlTypeToAPI(ControlType: TMMControlType): DWORD;
  474. function APIToControlType(ControlType: DWORD): TMMControlType;
  475. function APIToControlFlags(fdwControl: DWORD): TMMControlFlags;
  476. function APIToLineFlags(fdwLine: DWORD): TMMLineFlags;
  477. function ControlClassOfType(CT: DWORD): TMMControlClass;
  478. function IsControlTypeSingleSelect(CT: DWORD): Boolean;
  479. function ControlIdToIdent(Id: LongInt; var S: string): Boolean;
  480. function LineIdToIdent(Id: LongInt; var S: string): Boolean;
  481. function ItemIndexToIdent(Id: LongInt; var S: string): Boolean;
  482. function IdentToControlId(const S: string; var Id: LongInt): Boolean;
  483. function IdentToLineId(const S: string; var Id: LongInt): Boolean;
  484. function IdentToItemIndex(const S: string; var Id: LongInt): Boolean;
  485. type
  486.     EMMMixerServiceError = class(Exception)
  487.     end;
  488. {========================================================================}
  489. implementation
  490. {========================================================================}
  491. uses
  492.     MMDesign;
  493. {== Mixer service =======================================================}
  494. const
  495.     CompTypes: array[TMMComponentType] of DWORD =
  496.            (MIXERLINE_COMPONENTTYPE_DST_DIGITAL,
  497.             MIXERLINE_COMPONENTTYPE_DST_HEADPHONES,
  498.             MIXERLINE_COMPONENTTYPE_DST_LINE,
  499.             MIXERLINE_COMPONENTTYPE_DST_MONITOR,
  500.             MIXERLINE_COMPONENTTYPE_DST_SPEAKERS,
  501.             MIXERLINE_COMPONENTTYPE_DST_TELEPHONE,
  502.             MIXERLINE_COMPONENTTYPE_DST_UNDEFINED,
  503.             MIXERLINE_COMPONENTTYPE_DST_VOICEIN,
  504.             MIXERLINE_COMPONENTTYPE_DST_WAVEIN,
  505.             MIXERLINE_COMPONENTTYPE_SRC_ANALOG,
  506.             MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY,
  507.             MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC,
  508.             MIXERLINE_COMPONENTTYPE_SRC_DIGITAL,
  509.             MIXERLINE_COMPONENTTYPE_SRC_LINE,
  510.             MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE,
  511.             MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER,
  512.             MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER,
  513.             MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE,
  514.             MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED,
  515.             MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT);
  516. {------------------------------------------------------------------------}
  517. function APIToCompType(dwType: DWORD): TMMComponentType;
  518. var
  519.     i: TMMComponentType;
  520. begin
  521.     for i := Low(TMMComponentType) to High(TMMComponentType) do
  522.     if CompTypes[i] = dwType then
  523.     begin
  524.        Result := i;
  525.        Exit;
  526.     end;
  527.     { TODO: Should be resource id }
  528.     raise EMMMixerServiceError.Create('Undefined API component type');
  529. end;
  530. {------------------------------------------------------------------------}
  531. function CompTypeToAPI(CompType: TMMComponentType): DWORD;
  532. begin
  533.    Result := CompTypes[CompType];
  534. end;
  535. {------------------------------------------------------------------------}
  536. function MakeVersion(Maj, Min: Byte): WORD;
  537. begin
  538.    Result := MakeWord(Min, Maj);
  539. end;
  540. {------------------------------------------------------------------------}
  541. const
  542.     Targets: array[TMMAudioDeviceType] of UINT =
  543.            (MIXERLINE_TARGETTYPE_MIDIIN,
  544.             MIXERLINE_TARGETTYPE_MIDIOUT,
  545.             MIXERLINE_TARGETTYPE_WAVEIN,
  546.             MIXERLINE_TARGETTYPE_WAVEOUT,
  547.             MIXERLINE_TARGETTYPE_AUX,
  548.             MIXERLINE_TARGETTYPE_UNDEFINED);
  549. {------------------------------------------------------------------------}
  550. function DeviceTypeToTarget(DevType: TMMAudioDeviceType): UINT;
  551. begin
  552.    Result := Targets[DevType];
  553. end;
  554. {------------------------------------------------------------------------}
  555. const
  556.     Types: array[TMMControlType] of DWORD =
  557.            (MIXERCONTROL_CONTROLTYPE_CUSTOM,
  558.             MIXERCONTROL_CONTROLTYPE_BASS,
  559.             MIXERCONTROL_CONTROLTYPE_EQUALIZER,
  560.             MIXERCONTROL_CONTROLTYPE_FADER,
  561.             MIXERCONTROL_CONTROLTYPE_TREBLE,
  562.             MIXERCONTROL_CONTROLTYPE_VOLUME,
  563.             MIXERCONTROL_CONTROLTYPE_MIXER,
  564.             MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT,
  565.             MIXERCONTROL_CONTROLTYPE_MUX,
  566.             MIXERCONTROL_CONTROLTYPE_SINGLESELECT,
  567.             MIXERCONTROL_CONTROLTYPE_BOOLEANMETER,
  568.             MIXERCONTROL_CONTROLTYPE_PEAKMETER,
  569.             MIXERCONTROL_CONTROLTYPE_SIGNEDMETER,
  570.             MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER,
  571.             MIXERCONTROL_CONTROLTYPE_DECIBELS,
  572.             MIXERCONTROL_CONTROLTYPE_PERCENT,
  573.             MIXERCONTROL_CONTROLTYPE_SIGNED,
  574.             MIXERCONTROL_CONTROLTYPE_UNSIGNED,
  575.             MIXERCONTROL_CONTROLTYPE_PAN,
  576.             MIXERCONTROL_CONTROLTYPE_QSOUNDPAN,
  577.             MIXERCONTROL_CONTROLTYPE_SLIDER,
  578.             MIXERCONTROL_CONTROLTYPE_BOOLEAN,
  579.             MIXERCONTROL_CONTROLTYPE_BUTTON,
  580.             MIXERCONTROL_CONTROLTYPE_LOUDNESS,
  581.             MIXERCONTROL_CONTROLTYPE_MONO,
  582.             MIXERCONTROL_CONTROLTYPE_MUTE,
  583.             MIXERCONTROL_CONTROLTYPE_ONOFF,
  584.             MIXERCONTROL_CONTROLTYPE_STEREOENH,
  585.             MIXERCONTROL_CONTROLTYPE_MICROTIME,
  586.             MIXERCONTROL_CONTROLTYPE_MILLITIME);
  587. {------------------------------------------------------------------------}
  588. function ControlTypeToAPI(ControlType: TMMControlType): DWORD;
  589. begin
  590.    Result := Types[ControlType];
  591. end;
  592. {------------------------------------------------------------------------}
  593. function APIToControlType(ControlType: DWORD): TMMControlType;
  594. begin
  595.    for Result := Low(TMMControlType) to High(TMMControlType) do
  596.    if Types[Result] = ControlType then
  597.       Exit;
  598.    Result := ctCustom; { Because on my computer there are some undefined device }
  599. end;
  600. {------------------------------------------------------------------------}
  601. const
  602.     Flags: array[TMMControlFlag] of DWORD =
  603.        (MIXERCONTROL_CONTROLF_DISABLED,
  604.         MIXERCONTROL_CONTROLF_MULTIPLE,
  605.         MIXERCONTROL_CONTROLF_UNIFORM);
  606. {------------------------------------------------------------------------}
  607. function APIToControlFlags(fdwControl: DWORD): TMMControlFlags;
  608. var
  609.    i: TMMControlFlag;
  610. begin
  611.    Result:= [];
  612.    for i:= Low(TMMControlFlag) to High(TMMControlFlag) do
  613.    if (fdwControl and Flags[i]) <> 0 then
  614.        Include(Result,i);
  615. end;
  616. {------------------------------------------------------------------------}
  617. const
  618.     LineFlags: array[TMMLineFlag] of DWORD =
  619.        (MIXERLINE_LINEF_ACTIVE,
  620.         MIXERLINE_LINEF_DISCONNECTED,
  621.         MIXERLINE_LINEF_SOURCE);
  622. {------------------------------------------------------------------------}
  623. function APIToLineFlags(fdwLine: DWORD): TMMLineFlags;
  624. var
  625.    i: TMMLineFlag;
  626. begin
  627.    Result:= [];
  628.    for i:= Low(TMMLineFlag) to High(TMMLineFlag) do
  629.    if (fdwLine and LineFlags[i]) <> 0 then
  630.        Include(Result,i);
  631. end;
  632. {------------------------------------------------------------------------}
  633. const
  634.     CClasses: array[TMMControlClass] of DWORD =
  635.            (MIXERCONTROL_CT_CLASS_CUSTOM,
  636.             MIXERCONTROL_CT_CLASS_FADER,
  637.             MIXERCONTROL_CT_CLASS_LIST,
  638.             MIXERCONTROL_CT_CLASS_METER,
  639.             MIXERCONTROL_CT_CLASS_NUMBER,
  640.             MIXERCONTROL_CT_CLASS_SLIDER,
  641.             MIXERCONTROL_CT_CLASS_SWITCH,
  642.             MIXERCONTROL_CT_CLASS_TIME);
  643. {------------------------------------------------------------------------}
  644. function ControlClassOfType(CT: DWORD): TMMControlClass;
  645. begin
  646.    for Result:= Low(TMMControlClass) to High(TMMControlClass) do
  647.    if (CT and MIXERCONTROL_CT_CLASS_MASK) = CClasses[Result] then
  648.        Exit;
  649.    { TODO: Should be resource id }
  650.    raise EMMMixerServiceError.Create('Undefined API control class');
  651. end;
  652. {------------------------------------------------------------------------}
  653. function IsControlTypeSingleSelect(CT: DWORD): Boolean;
  654. begin
  655.     Result := MIXERCONTROL_CT_SC_LIST_SINGLE =
  656.               (MIXERCONTROL_CT_SUBCLASS_MASK and CT);
  657. end;
  658. {== TMMMixerDevice ======================================================}
  659. constructor TMMMixerDevice.Create(AOwner: TComponent);
  660. begin
  661.    inherited Create(AOwner);
  662.    FObserver          := TMMObserver.Create;
  663.    FObserver.OnNotify := DeviceNotify;
  664.    FObservable        := TMMObservable.Create;
  665.    DeviceType         := dtMixer;
  666. end;
  667. {-- TMMMixerDevice ------------------------------------------------------}
  668. destructor TMMMixerDevice.Destroy;
  669. begin
  670.    Device := nil;
  671.    FObserver.Free;
  672.    FObservable.Free;
  673.    inherited Destroy;
  674. end;
  675. {-- TMMMixerDevice ------------------------------------------------------}
  676. procedure TMMMixerDevice.SetDevice(Value: TMMCustomAudioDevice);
  677. var
  678.     WasActive: Boolean;
  679. begin
  680.    if (Value <> nil) then
  681.       if (Value.GetDeviceType = dtMixer) then
  682.          { TODO: Should be resource id }
  683.          raise EMMMixerDeviceError.Create('Mixer can''t reference mixer');
  684.    if (Value <> FDevice) then
  685.    begin
  686.       WasActive:= Active;
  687.       Close;
  688.       if (FDevice <> nil) then
  689.           FDevice.RemoveObserver(FObserver);
  690.       FDevice:= Value;
  691.       if (FDevice <> nil) then
  692.       begin
  693.          FDevice.AddObserver(FObserver);
  694.          FDevice.FreeNotification(Self);
  695.       end;
  696.       UpdateDevice;
  697.       Active:= WasActive;
  698.    end;
  699. end;
  700. {-- TMMMixerDevice ------------------------------------------------------}
  701. procedure TMMMixerDevice.DeviceNotify(Sender, Data: TObject);
  702. begin
  703.    UpdateDevice;
  704. end;
  705. {-- TMMMixerDevice ------------------------------------------------------}
  706. procedure TMMMixerDevice.Notification(AComponent: TComponent; Operation: TOperation);
  707. begin
  708.    inherited Notification(AComponent,Operation);
  709.    if (Operation = opRemove) and (AComponent = FDevice) then
  710.        Device := nil;
  711. end;
  712. {-- TMMMixerDevice ------------------------------------------------------}
  713. procedure TMMMixerDevice.UpdateDevice;
  714. begin
  715.    if (FDevice <> nil) then
  716.        DeviceId:= FDevice.MixerId;
  717.    inherited UpdateDevice;
  718. end;
  719. {-- TMMMixerDevice ------------------------------------------------------}
  720. procedure TMMMixerDevice.Changed;
  721. var
  722.    UpdChange: TMMDeviceChange;
  723. begin
  724.    UpdChange:= TMMDeviceChange.Create;
  725.    try
  726.       FObservable.NotifyObservers(UpdChange);
  727.    finally
  728.       UpdChange.Free;
  729.    end;
  730.    inherited;
  731. end;
  732. {-- TMMMixerDevice ------------------------------------------------------}
  733. function TMMMixerDevice.StoreDeviceId: Boolean;
  734. begin
  735.    if (FDevice <> nil) then
  736.        Result := FDevice.MixerId <> MixerId
  737.    else
  738.        Result := DeviceId <> 0;
  739. end;
  740. {-- TMMMixerDevice ------------------------------------------------------}
  741. function TMMMixerDevice.GetDestinations: TMMLineIndex;
  742. var
  743.    Caps: TMixerCaps;
  744. begin
  745.    if ValidDevice then
  746.    begin
  747.       Check(mixerGetDevCaps(DeviceId, @Caps, SizeOf(Caps)));
  748.       Result:= Caps.cDestinations;
  749.    end
  750.    else Result := 0;
  751. end;
  752. {-- TMMMixerDevice ------------------------------------------------------}
  753. procedure   TMMMixerDevice.Open;
  754. begin
  755.    if ValidDevice then
  756.    begin
  757.       FCBWnd:= AllocateHWnd(MixerWndProc);
  758.       try
  759.          Check(mixerOpen(@FHandle,DeviceId,FCBWnd,0,CALLBACK_WINDOW or MIXER_OBJECTF_MIXER));
  760.          inherited Open;
  761.       except
  762.          DeAllocateHWnd(FCBWnd);
  763.          FCBWnd:= 0;
  764.       end
  765.    end
  766.    else
  767.       { TODO: Should be resource id }
  768.       raise EMMMixerDeviceError.Create('Device not valid');
  769. end;
  770. {-- TMMMixerDevice ------------------------------------------------------}
  771. procedure TMMMixerDevice.Close;
  772. begin
  773.    if FHandle <> 0 then
  774.    begin
  775.       Check(mixerClose(FHandle));
  776.       FHandle:= 0;
  777.    end;
  778.    if FCBWnd <> 0 then
  779.    begin
  780.       DeAllocateHWnd(FCBWnd);
  781.       FCBWnd:= 0;
  782.    end;
  783.    inherited Close;
  784. end;
  785. {-- TMMMixerDevice ------------------------------------------------------}
  786. procedure TMMMixerDevice.MixerWndProc(var Msg: TMessage);
  787. begin
  788.    with Msg do
  789.    begin
  790.       try
  791.          if (Msg = MM_MIXM_LINE_CHANGE) and (wParam = FHandle) then
  792.              LineChanged(lParam)
  793.          else if (Msg = MM_MIXM_CONTROL_CHANGE) and (wParam = FHandle) then
  794.              ControlChanged(lParam)
  795.          else
  796.              Result:= DefWindowProc(FCBWnd, Msg, wParam, lParam);
  797.       except
  798.          Application.HandleException(Self);
  799.       end;
  800.    end;
  801. end;
  802. {-- TMMMixerDevice ------------------------------------------------------}
  803. procedure TMMMixerDevice.LineChanged(LineId: TMMLineId);
  804. var
  805.     Chg: TMMLineChange;
  806. begin
  807.    Chg:= TMMLineChange.Create;
  808.    try
  809.       Chg.LineId := LineId;
  810.       FObservable.NotifyObservers(Chg);
  811.    finally
  812.       Chg.Free;
  813.    end;
  814.    DoLineChange(LineId);
  815. end;
  816. {-- TMMMixerDevice ------------------------------------------------------}
  817. procedure TMMMixerDevice.ControlChanged(CtlId: TMMControlId);
  818. var
  819.     Chg   : TMMControlChange;
  820.     LineId: TMMLineId;
  821. begin
  822.    LineId := GetLineOfControl(CtlId);
  823.    Chg := TMMControlChange.Create;
  824.    try
  825.       Chg.ControlId:= CtlId;
  826.       Chg.LineId   := LineId;
  827.       FObservable.NotifyObservers(Chg);
  828.    finally
  829.       Chg.Free;
  830.    end;
  831.    DoControlChange(LineId,CtlId);
  832. end;
  833. {-- TMMMixerDevice ------------------------------------------------------}
  834. procedure TMMMixerDevice.DoLineChange(LineId: TMMLineId);
  835. begin
  836.    if Assigned(FOnLineChange) then FOnLineChange(Self,LineId);
  837. end;
  838. {-- TMMMixerDevice ------------------------------------------------------}
  839. procedure TMMMixerDevice.DoControlChange(LineId: TMMLineId; CtlId: TMMControlId);
  840. begin
  841.    if Assigned(FOnControlChange) then FOnControlChange(Self,LineId,CtlId);
  842. end;
  843. {-- TMMMixerDevice ------------------------------------------------------}
  844. function TMMMixerDevice.GetMixerId: TMMDeviceId;
  845. var
  846.    Res: UINT;
  847. begin
  848.    if FHandle <> 0 then
  849.    begin
  850.       Check(mixerGetId(FHandle, Res, MIXER_OBJECTF_HMIXER));
  851.       Result := Res;
  852.       { If device id was changed, FHandle will be still correct but }
  853.       { we should check for id }
  854.       if (Result <> DeviceId) then
  855.       begin
  856.          { Just update class member }
  857.          SetDeviceIdDirect(Result);
  858.          { Notify updates }
  859.          Changed;
  860.       end;
  861.    end
  862.    else Result := DeviceId;
  863. end;
  864. {-- TMMMixerDevice ------------------------------------------------------}
  865. procedure TMMMixerDevice.AddObserver(O: TMMObserver);
  866. begin
  867.    FObservable.AddObserver(O);
  868. end;
  869. {-- TMMMixerDevice ------------------------------------------------------}
  870. procedure TMMMixerDevice.RemoveObserver(O: TMMObserver);
  871. begin
  872.    if (FObservable <> nil) then
  873.        FObservable.RemoveObserver(O);
  874. end;
  875. {-- TMMMixerDevice ------------------------------------------------------}
  876. function TMMMixerDevice.GetLineOfControl(CtlId: TMMControlId): TMMLineId;
  877. var
  878.    Ctls: TMixerLineControls;
  879.    Ctl : TMixerControl;
  880. begin
  881.    { We should do this call manually instead of use GetControlInfoById
  882.      because we need to access dwLineId member of TMixerLineControls record }
  883.    Ctls.cbStruct      := SizeOf(Ctls);
  884.    Ctls.dwControlId   := CtlId;
  885.    Ctls.cControls     := 1;
  886.    Ctls.cbmxCtrl      := sizeof(TMixerControl);
  887.    Ctls.pamxCtrl      := @Ctl;
  888.    Check(mixerGetLineControls(MixerId, @Ctls, MIXER_GETLINECONTROLSF_ONEBYID or MIXER_OBJECTF_MIXER));
  889.    Result             := Ctls.dwLineId;
  890. end;
  891. {-- TMMMixerDevice ------------------------------------------------------}
  892. function TMMMixerDevice.GetLineInfoById(LineId: TMMLineId; var Info: TMixerLine): Boolean;
  893. begin
  894.    Info.dwLineId := LineId;
  895.    Result        := GetLineInfo(Info,MIXER_GETLINEINFOF_LINEID);
  896. end;
  897. {-- TMMMixerDevice ------------------------------------------------------}
  898. function TMMMixerDevice.GetLineInfoByCompType(CompType: TMMComponentType; var Info: TMixerLine): Boolean;
  899. begin
  900.    Info.dwComponentType:= CompTypeToAPI(CompType);
  901.    Result              := GetLineInfo(Info,MIXER_GETLINEINFOF_COMPONENTTYPE);
  902. end;
  903. {-- TMMMixerDevice ------------------------------------------------------}
  904. function TMMMixerDevice.GetLineInfoByDestination(Dest: TMMLineIndex; var Info: TMixerLine): Boolean;
  905. begin
  906.    Info.dwDestination := Dest;
  907.    Result             := GetLineInfo(Info,MIXER_GETLINEINFOF_DESTINATION);
  908. end;
  909. {-- TMMMixerDevice ------------------------------------------------------}
  910. function TMMMixerDevice.GetLineInfoBySource(Dest, Src: TMMLineIndex; var Info: TMixerLine): Boolean;
  911. begin
  912.    Info.dwDestination := Dest;
  913.    Info.dwSource      := Src;
  914.    Result             := GetLineInfo(Info,MIXER_GETLINEINFOF_SOURCE);
  915. end;
  916. {-- TMMMixerDevice ------------------------------------------------------}
  917. function TMMMixerDevice.GetLineInfo(var Info: TMixerLine; Flags: DWORD): Boolean;
  918. begin
  919.    Info.cbStruct:= SizeOf(Info);
  920.    Result:= CheckExcl(mixerGetLineInfo(MixerId, @Info, MIXER_OBJECTF_MIXER or Flags), [MIXERR_INVALLINE,MMSYSERR_NODRIVER,MMSYSERR_INVALPARAM])
  921.             = MMSYSERR_NOERROR;
  922. end;
  923. {-- TMMMixerDevice ------------------------------------------------------}
  924. function TMMMixerDevice.GetLineInfoByTarget(Device: TMMCustomAudioDevice; var Info: TMixerLine): Boolean;
  925. begin
  926.    with Device.DeviceCaps do
  927.    begin
  928.       Info.Target.dwType         := DeviceTypeToTarget(Device.GetDeviceType);
  929.       Info.Target.wMid           := ManufacturerId;
  930.       Info.Target.wPid           := ProductId;
  931.       Info.Target.vDriverVersion := MakeVersion(VerMajor,VerMinor);
  932.       StrPLCopy(Info.Target.szPName, ProductName, MAXPNAMELEN);
  933.       Result:= GetLineInfo(Info, MIXER_GETLINEINFOF_TARGETTYPE);
  934.    end;
  935. end;
  936. {-- TMMMixerDevice ------------------------------------------------------}
  937. function TMMMixerDevice.GetControlInfoById(ControlId: TMMControlId; var Info: TMixerControl): Boolean;
  938. var
  939.     Ctls: TMixerLineControls;
  940. begin
  941.    Ctls.dwControlId:= ControlId;
  942.    Ctls.cControls  := 1;
  943.    Ctls.cbmxCtrl   := SizeOf(Info);
  944.    Ctls.pamxCtrl   := @Info;
  945.    Result:= GetControlInfo(Ctls, MIXER_GETLINECONTROLSF_ONEBYID);
  946. end;
  947. {-- TMMMixerDevice ------------------------------------------------------}
  948. function TMMMixerDevice.GetControlInfoByType(LineId: TMMLineId; ControlType: TMMControlType; var Info: TMixerControl): Boolean;
  949. var
  950.     Ctls: TMixerLineControls;
  951. begin
  952.    Ctls.dwLineId      := LineId;
  953.    Ctls.dwControlType := ControlTypeToAPI(ControlType);
  954.    Ctls.cControls     := 1;
  955.    Ctls.cbmxCtrl      := SizeOf(Info);
  956.    Ctls.pamxCtrl      := @Info;
  957.    Result:= GetControlInfo(Ctls, MIXER_GETLINECONTROLSF_ONEBYTYPE);
  958. end;
  959. {-- TMMMixerDevice ------------------------------------------------------}
  960. procedure TMMMixerDevice.GetAllControls(LineId: TMMLineId; Controls: TMMControlIndex; P: PMixerControl);
  961. var
  962.    Ctls: TMixerLineControls;
  963. begin
  964.    Ctls.dwLineId      := LineId;
  965.    Ctls.cControls     := Controls;
  966.    Ctls.cbmxCtrl      := SizeOf(P^);
  967.    Ctls.pamxCtrl      := PMixerControlA(P);
  968.    if not GetControlInfo(Ctls, MIXER_GETLINECONTROLSF_ALL) then
  969.       { TODO: Should be resource id }
  970.       raise EMMMixerDeviceError.Create('Error requesting all controls');
  971. end;
  972. {-- TMMMixerDevice ------------------------------------------------------}
  973. function TMMMixerDevice.GetControlInfo(var Info: TMixerLineControls; Flags: DWORD): Boolean;
  974. begin
  975.    Info.cbStruct:= SizeOf(Info);
  976.    Result:= CheckExcl(mixerGetLineControls(MixerId,@Info,MIXER_OBJECTF_MIXER or Flags), [MIXERR_INVALCONTROL,MIXERR_INVALLINE,MMSYSERR_NODRIVER])
  977.             = MMSYSERR_NOERROR;
  978. end;
  979. {-- TMMMixerDevice ------------------------------------------------------}
  980. function TMMMixerDevice.GetControlByType(LineId: TMMLineId; ControlType: TMMControlType): TMMControlId;
  981. var
  982.    Info: TMixerControl;
  983. begin
  984.    if GetControlInfoByType(LineId, ControlType, Info) then
  985.       Result:= Info.dwControlId
  986.    else
  987.       Result:= badControlId;
  988. end;
  989. {-- TMMMixerDevice ------------------------------------------------------}
  990. function TMMMixerDevice.GetDestination(Index: TMMLineIndex): TMMLineId;
  991. var
  992.     Info: TMixerLine;
  993. begin
  994.    if GetLineInfoByDestination(Index,Info) then
  995.       Result:= Info.dwLineId
  996.    else
  997.       Result:= badLineId;
  998. end;
  999. {-- TMMMixerDevice ------------------------------------------------------}
  1000. procedure TMMMixerDevice.SetBooleanControl(Id: TMMControlId; Channels: TMMChannelIndex; Items: TMMItemIndex; Values: PBoolean);
  1001. var
  1002.    Buf, P : PMixerControlDetailsBoolean;
  1003.    i      : Integer;
  1004.    N      : Integer;
  1005. begin
  1006.    N  := Max(Channels,1) * Max(Items,1);
  1007.    Buf:= GlobalAllocMem(SizeOf(P^) * N);
  1008.    try
  1009.       P:= Buf;
  1010.       for i:= 0 to N - 1 do
  1011.       begin
  1012.          P^.fValue:= Ord(Values^);
  1013.          Inc(P);
  1014.          Inc(Values);
  1015.       end;
  1016.       SetControlValues(Id, SizeOf(P^), Channels, Items, Buf, MIXER_SETCONTROLDETAILSF_VALUE);
  1017.    finally
  1018.       GlobalFreeMem(Pointer(Buf));
  1019.    end;
  1020. end;
  1021. {-- TMMMixerDevice ------------------------------------------------------}
  1022. procedure TMMMixerDevice.GetBooleanControl(Id: TMMControlId; Channels: TMMChannelIndex; Items: TMMItemIndex; Values: PBoolean);
  1023. var
  1024.    Buf, P : PMixerControlDetailsBoolean;
  1025.    i      : Integer;
  1026.    N      : Integer;
  1027. begin
  1028.    N  := Max(Channels,1) * Max(Items,1);
  1029.    Buf:= GlobalAllocMem(SizeOf(P^) * N);
  1030.    try
  1031.       P:= Buf;
  1032.       GetControlValues(Id, SizeOf(P^), Channels, Items, P, MIXER_GETCONTROLDETAILSF_VALUE);
  1033.       for i:= 0 to N - 1 do
  1034.       begin
  1035.          Values^:= Boolean(P^.fValue);
  1036.          Inc(P);
  1037.          Inc(Values);
  1038.       end;
  1039.    finally
  1040.       GlobalFreeMem(Pointer(Buf));
  1041.    end;
  1042. end;
  1043. {-- TMMMixerDevice ------------------------------------------------------}
  1044. procedure TMMMixerDevice.SetSignedControl(Id: TMMControlId; Channels: TMMChannelIndex; Items: TMMItemIndex; Values: PInteger);
  1045. var
  1046.    Buf, P : PMixerControlDetailsSigned;
  1047.    i      : Integer;
  1048.    N      : Integer;
  1049. begin
  1050.    N  := Max(Channels,1) * Max(Items,1);
  1051.    Buf:= GlobalAllocMem(SizeOf(P^) * N);
  1052.    try
  1053.       P:= Buf;
  1054.       for i:= 0 to N - 1 do
  1055.       begin
  1056.          P^.lValue:= Values^;
  1057.          Inc(P);
  1058.          Inc(Values);
  1059.       end;
  1060.       SetControlValues(Id, SizeOf(P^), Channels, Items, Buf, MIXER_SETCONTROLDETAILSF_VALUE);
  1061.    finally
  1062.       GlobalFreeMem(Pointer(Buf));
  1063.    end;
  1064. end;
  1065. {-- TMMMixerDevice ------------------------------------------------------}
  1066. procedure TMMMixerDevice.GetSignedControl(Id: TMMControlId; Channels: TMMChannelIndex; Items: TMMItemIndex; Values: PInteger);
  1067. var
  1068.    Buf, P : PMixerControlDetailsSigned;
  1069.    i      : Integer;
  1070.    N      : Integer;
  1071. begin
  1072.    N  := Max(Channels,1) * Max(Items,1);
  1073.    Buf:= GlobalAllocMem(SizeOf(P^) * N);
  1074.    try
  1075.       P:= Buf;
  1076.       GetControlValues(Id, SizeOf(P^), Channels, Items, P, MIXER_GETCONTROLDETAILSF_VALUE);
  1077.       for i:= 0 to N - 1 do
  1078.       begin
  1079.          Values^:= P^.lValue;
  1080.          Inc(P);
  1081.          Inc(Values);
  1082.       end;
  1083.    finally
  1084.       GlobalFreeMem(Pointer(Buf));
  1085.    end;
  1086. end;
  1087. {-- TMMMixerDevice ------------------------------------------------------}
  1088. procedure TMMMixerDevice.SetUnsignedControl(Id: TMMControlId; Channels: TMMChannelIndex; Items: TMMItemIndex; Values: PCardinal);
  1089. var
  1090.    Buf, P : PMixerControlDetailsUnsigned;
  1091.    i      : Integer;
  1092.    N      : Integer;
  1093. begin
  1094.    N  := Max(Channels,1) * Max(Items,1);
  1095.    Buf:= GlobalAllocMem(SizeOf(P^) * N);
  1096.    try
  1097.       P:= Buf;
  1098.       for i:= 0 to N - 1 do
  1099.       begin
  1100.          P^.dwValue:= Values^;
  1101.          Inc(P);
  1102.          Inc(Values);
  1103.       end;
  1104.       SetControlValues(Id, SizeOf(P^), Channels, Items, Buf, MIXER_SETCONTROLDETAILSF_VALUE);
  1105.    finally
  1106.       GlobalFreeMem(Pointer(Buf));
  1107.    end;
  1108. end;
  1109. {-- TMMMixerDevice ------------------------------------------------------}
  1110. procedure TMMMixerDevice.GetUnsignedControl(Id: TMMControlId; Channels: TMMChannelIndex; Items: TMMItemIndex; Values: PCardinal);
  1111. var
  1112.    Buf, P : PMixerControlDetailsUnsigned;
  1113.    i      : Integer;
  1114.    N      : Integer;
  1115. begin
  1116.    N  := Max(Channels,1) * Max(Items,1);
  1117.    Buf:= GlobalAllocMem(SizeOf(P^) * N);
  1118.    try
  1119.       P:= Buf;
  1120.       GetControlValues(Id, SizeOf(P^), Channels, Items, P, MIXER_GETCONTROLDETAILSF_VALUE);
  1121.       for i:= 0 to N - 1 do
  1122.       begin
  1123.          Values^:= P^.dwValue;
  1124.          Inc(P);
  1125.          Inc(Values);
  1126.       end;
  1127.    finally
  1128.       GlobalFreeMem(Pointer(Buf));
  1129.    end;
  1130. end;
  1131. {-- TMMMixerDevice ------------------------------------------------------}
  1132. procedure TMMMixerDevice.GetItemsInfo(Id: TMMControlId; Channels: TMMChannelIndex; Items: TMMItemIndex; Infos: PMixerControlDetailsListText);
  1133. var
  1134.    Details: TMixerControlDetails;
  1135. begin
  1136.    FillChar(Details,SizeOf(Details),0);
  1137.    Details.cbStruct       := SizeOf(Details);
  1138.    Details.cbDetails      := SizeOf(Infos^);
  1139.    Details.paDetails      := Infos;
  1140.    Details.dwControlId    := Id;
  1141.    Details.cMultipleItems := Items;
  1142.    Details.cChannels      := Channels;
  1143.    Check(mixerGetControlDetails(MixerId, @Details, MIXER_OBJECTF_MIXER or MIXER_GETCONTROLDETAILSF_LISTTEXT));
  1144. end;
  1145. {-- TMMMixerDevice ------------------------------------------------------}
  1146. procedure TMMMixerDevice.GetControlValues(Id: TMMControlId; ItemSize: DWORD; Channels: TMMChannelIndex; Items: TMMItemIndex; Values: Pointer; Flags: DWORD);
  1147. var
  1148.    Details    : TMixerControlDetails;
  1149. begin
  1150.    FillChar(Details,SizeOf(Details),0);
  1151.    Details.cbStruct       := SizeOf(Details);
  1152.    Details.cbDetails      := ItemSize;
  1153.    Details.paDetails      := Values;
  1154.    Details.dwControlId    := Id;
  1155.    Details.cMultipleItems := Items;
  1156.    Details.cChannels      := Channels;
  1157.    Check(mixerGetControlDetails(MixerId, @Details, MIXER_OBJECTF_MIXER or Flags));
  1158. end;
  1159. {-- TMMMixerDevice ------------------------------------------------------}
  1160. procedure TMMMixerDevice.SetControlValues(Id: TMMControlId; ItemSize: DWORD; Channels: TMMChannelIndex; Items: TMMItemIndex; Values: Pointer; Flags: DWORD);
  1161. var
  1162.    Details    : TMixerControlDetails;
  1163. begin
  1164.    FillChar(Details,SizeOf(Details),0);
  1165.    Details.cbStruct       := SizeOf(Details);
  1166.    Details.cbDetails      := ItemSize;
  1167.    Details.paDetails      := Values;
  1168.    Details.dwControlId    := Id;
  1169.    Details.cMultipleItems := Items;
  1170.    Details.cChannels      := Channels;
  1171.    Check(mixerSetControlDetails(MixerId, @Details, MIXER_OBJECTF_MIXER or Flags));
  1172. end;
  1173. {== TMMLineInfo =========================================================}
  1174. procedure TMMLineInfo.SetDummyStr(const Value: string);
  1175. begin
  1176.   ;
  1177. end;
  1178. {-- TMMLineInfo ---------------------------------------------------------}
  1179. procedure TMMLineInfo.Clear;
  1180. begin
  1181.    FFlags       := [];
  1182.    FChannels    := 0;
  1183.    FConnections := 0;
  1184.    FControls    := 0;
  1185.    FShortName   := '';
  1186.    FName        := '';
  1187. end;
  1188. {== TMMAudioLine ========================================================}
  1189. constructor TMMAudioLine.Create(AOwner: TComponent);
  1190. begin
  1191.    inherited Create(AOwner);
  1192.    FLineInfo               := TMMLineInfo.Create;
  1193.    FObserver               := TMMObserver.Create;
  1194.    FObserver.OnNotify      := MixerNotify;
  1195.    FTargetObserver         := TMMObserver.Create;
  1196.    FTargetObserver.OnNotify:= TargetNotify;
  1197.    FDestObserver           := TMMObserver.Create;
  1198.    FDestObserver.OnNotify  := DestNotify;
  1199.    FLineId                 := badLineId;
  1200.    FObservable             := TMMObservable.Create;
  1201. end;
  1202. {-- TMMAudioLine --------------------------------------------------------}
  1203. destructor TMMAudioLine.Destroy;
  1204. begin
  1205.    FObserver.Free;
  1206.    FTargetObserver.Free;
  1207.    FDestObserver.Free;
  1208.    FObservable.Free;
  1209.    FObservable:= nil;
  1210.    FLineInfo.Free;
  1211.    inherited Destroy;
  1212. end;
  1213. {-- TMMAudioLine --------------------------------------------------------}
  1214. procedure TMMAudioLine.Notification(AComponent: TComponent; Operation: TOperation);
  1215. begin
  1216.    inherited Notification(AComponent,Operation);
  1217.    if (Operation = opRemove) then
  1218.    begin
  1219.       if (AComponent = FMixer) then
  1220.          Mixer:= nil
  1221.       else if (AComponent = FTarget) then
  1222.          Target:= nil
  1223.       else if (AComponent = FDestLine) then
  1224.          DestLine := nil
  1225.    end;
  1226. end;
  1227. {-- TMMAudioLine --------------------------------------------------------}
  1228. procedure TMMAudioLine.SetMixer(Value: TMMMixerDevice);
  1229. begin
  1230.    if (Value <> FMixer) then
  1231.    begin
  1232.       if (FMixer <> nil) then
  1233.          FMixer.RemoveObserver(FObserver);
  1234.       FMixer:= Value;
  1235.       if (FMixer <> nil) then
  1236.       begin
  1237.          FMixer.AddObserver(FObserver);
  1238.          FMixer.FreeNotification(Self);
  1239.       end;
  1240.       UpdateLine;
  1241.    end;
  1242. end;
  1243. {-- TMMAudioLine --------------------------------------------------------}
  1244. procedure TMMAudioLine.SetLineId(Value: TMMLineId);
  1245. begin
  1246.    if (Value <> FLineId) then
  1247.    begin
  1248.       FLineId    := Value;
  1249.       FLineSetup := lsLineId;
  1250.       UpdateLine;
  1251.    end;
  1252. end;
  1253. {-- TMMAudioLine --------------------------------------------------------}
  1254. procedure TMMAudioLine.SetComponentType(Value: TMMComponentType);
  1255. begin
  1256.    if (Value <> FComponentType) then
  1257.    begin
  1258.       FComponentType := Value;
  1259.       FLineSetup     := lsCompType;
  1260.       UpdateLine;
  1261.    end;
  1262. end;
  1263. {-- TMMAudioLine --------------------------------------------------------}
  1264. procedure TMMAudioLine.SetTarget(Value: TMMCustomAudioDevice);
  1265. begin
  1266.    if (Value <> nil) and (Value.GetDeviceType = dtMixer) then
  1267.        { TODO: Should be resource id }
  1268.        raise EMMMixerDeviceError.Create('Can''t target to mixer');
  1269.    if Value <> FTarget then
  1270.    begin
  1271.       if FTarget <> nil then
  1272.          FTarget.RemoveObserver(FTargetObserver);
  1273.       FTarget:= Value;
  1274.       if FTarget <> nil then
  1275.       begin
  1276.          FTarget.AddObserver(FTargetObserver);
  1277.          FTarget.FreeNotification(Self);
  1278.       end;
  1279.       FLineSetup := lsTarget;
  1280.       UpdateLine;
  1281.    end;
  1282. end;
  1283. {-- TMMAudioLine --------------------------------------------------------}
  1284. function TMMAudioLine.StoreLineId: Boolean;
  1285. begin
  1286.    Result:= (FLineSetup = lsLineId) and (FLineId <> badLineId);
  1287. end;
  1288. {-- TMMAudioLine --------------------------------------------------------}
  1289. function TMMAudioLine.StoreComponentType: Boolean;
  1290. begin
  1291.    Result := (FLineSetup = lsCompType) and (FComponentType <> Low(TMMComponentType));
  1292. end;
  1293. {-- TMMAudioLine --------------------------------------------------------}
  1294. function TMMAudioLine.StoreTarget: Boolean;
  1295. begin
  1296.    Result := (FLineSetup = lsTarget) and (FTarget <> nil);
  1297. end;
  1298. {-- TMMAudioLine --------------------------------------------------------}
  1299. procedure TMMAudioLine.SetLineInfo(const Info: TMMLineInfo);
  1300. begin
  1301.   ;
  1302. end;
  1303. {-- TMMAudioLine --------------------------------------------------------}
  1304. function TMMAudioLine.GetConnections: TMMConnectionIndex;
  1305. begin
  1306.    Result := LineInfo.Connections;
  1307. end;
  1308. {-- TMMAudioLine --------------------------------------------------------}
  1309. function TMMAudioLine.GetConnection(Index: TMMConnectionIndex): TMMLineId;
  1310. var
  1311.     Info: TMixerLine;
  1312. begin
  1313.    if not InRange(Index, 0, Connections - 1) then
  1314.       { TODO: Should be resource id }
  1315.       raise EMMAudioLineError.Create('Connection index out of range');
  1316.    if ValidMixer then
  1317.    begin
  1318.       if Mixer.GetLineInfoBySource(FDestinationId, Index, Info) then
  1319.       begin
  1320.          Result:= Info.dwLineId;
  1321.          Exit;
  1322.       end;
  1323.    end;
  1324.    Result:= badLineId;
  1325. end;
  1326. {-- TMMAudioLine --------------------------------------------------------}
  1327. function TMMAudioLine.GetControls: TMMControlIndex;
  1328. begin
  1329.    Result := LineInfo.Controls;
  1330. end;
  1331. {-- TMMAudioLine --------------------------------------------------------}
  1332. function TMMAudioLine.GetControl(Index: TMMControlIndex): TMMControlId;
  1333. var
  1334.     P, R: PMixerControl;
  1335.     Size: Integer;
  1336. begin
  1337.    if not InRange(Index, 0, Controls - 1) then
  1338.       { TODO: Should be resource id }
  1339.       raise EMMAudioLineError.Create('Control index out of range');
  1340.    if not ValidMixer then
  1341.    begin
  1342.       Result:= badControlId;
  1343.       Exit;
  1344.    end;
  1345.    Size:= SizeOf(P^)*Controls;
  1346.    GetMem(P,Size);
  1347.    try
  1348.       Mixer.GetAllControls(LineId,Controls,P);
  1349.       R:= P;
  1350.       Inc(R,Index);
  1351.       Result:= R^.dwControlId;
  1352.    finally
  1353.       FreeMem(P,Size);
  1354.    end;
  1355. end;
  1356. {-- TMMAudioLine --------------------------------------------------------}
  1357. function TMMAudioLine.ValidMixer: Boolean;
  1358. begin
  1359.    Result:= (FMixer <> nil) and FMixer.ValidDevice;
  1360. end;
  1361. {-- TMMAudioLine --------------------------------------------------------}
  1362. procedure TMMAudioLine.UpdateLine;
  1363. var
  1364.     LineInfo: TMixerLine;
  1365.     Ok: Boolean;
  1366. begin
  1367.    if csLoading in ComponentState then
  1368.       Exit;
  1369.    FDestinationId:= badLineId;
  1370.    Ok := False;
  1371.    if ValidMixer then
  1372.    begin
  1373.       if FDestLine <> nil then
  1374.         if FDestLine.Mixer = Mixer then
  1375.             Ok := FDestLine.GetLineInfoForSource(Self,LineInfo)
  1376.         else
  1377.             Ok := False
  1378.       else
  1379.       case FLineSetup of
  1380.         lsLineId   : Ok:= (FLineId <> badLineId) and Mixer.GetLineInfoById(FLineId, LineInfo);
  1381.         lsCompType : Ok:= Mixer.GetLineInfoByCompType(FComponentType, LineInfo);
  1382.         lsTarget   : Ok:= Mixer.GetLineInfoByTarget(FTarget, LineInfo);
  1383.       end;
  1384.       if Ok then
  1385.       begin
  1386.          FLineId        := LineInfo.dwLineId;
  1387.          FComponentType := APIToCompType(LineInfo.dwComponentType);
  1388.          with FLineInfo do
  1389.          begin
  1390.             FFlags         := APIToLineFlags(LineInfo.fdwLine);
  1391.             FChannels      := LineInfo.cChannels;
  1392.             FConnections   := LineInfo.cConnections;
  1393.             FControls      := LineInfo.cControls;
  1394.             if not (cfSource in FFlags) then
  1395.                FDestinationId := LineInfo.dwDestination
  1396.             else
  1397.                FDestinationId := badLineId;
  1398.             FShortName     := LineInfo.szShortName;
  1399.             FName          := LineInfo.szName;
  1400.          end;
  1401.       end
  1402.       else
  1403.       begin
  1404.          FLineId := badLineId;
  1405.          FLineInfo.Clear;
  1406.       end;
  1407.    end;
  1408.    LineIdChanged;
  1409. end;
  1410. {-- TMMAudioLine --------------------------------------------------------}
  1411. procedure TMMAudioLine.Loaded;
  1412. begin
  1413.    inherited Loaded;
  1414.    UpdateLine;
  1415. end;
  1416. {-- TMMAudioLine --------------------------------------------------------}
  1417. procedure TMMAudioLine.MixerNotify(Sender, Data: TObject);
  1418. begin
  1419.    if Data is TMMLineChange then
  1420.    begin
  1421.       if (Data as TMMLineChange).LineId = FLineId then
  1422.           Changed;
  1423.    end
  1424.    else if Data is TMMControlChange then
  1425.    begin
  1426.       if (Data as TMMControlChange).LineId = FLineId then
  1427.           ControlChanged((Data as TMMControlChange).ControlId)
  1428.    end
  1429.    else if Data is TMMDeviceChange then
  1430.    begin
  1431.       UpdateLine;
  1432.    end;
  1433. end;
  1434. {-- TMMAudioLine --------------------------------------------------------}
  1435. procedure TMMAudioLine.TargetNotify(Sender, Data: TObject);
  1436. begin
  1437.    UpdateLine;
  1438. end;
  1439. {-- TMMAudioLine --------------------------------------------------------}
  1440. procedure TMMAudioLine.DestNotify(Sender, Data: TObject);
  1441. begin
  1442.     if (Data <> nil) and (Data is TMMLineIdChange) then
  1443.         UpdateLine;
  1444. end;
  1445. {-- TMMAudioLine --------------------------------------------------------}
  1446. procedure TMMAudioLine.SetDestLine(Value: TMMComponent1);
  1447. begin
  1448.     if (Value <> nil) and not (Value is TMMAudioLine) then
  1449.         { TODO: Should be resource id }
  1450.         raise EMMMixerDeviceError.Create('DestLine should be TMMAudioLine');
  1451.     if FDestLine <> Value then
  1452.     begin
  1453.         if FDestLine <> nil then
  1454.             FDestLine.RemoveObserver(FDestObserver);
  1455.         FDestLine := Value as TMMAudioLine;
  1456.         if FDestLine <> nil then
  1457.         begin
  1458.             FDestLine.AddObserver(FTargetObserver);
  1459.             FDestLine.FreeNotification(Self);
  1460.         end;
  1461.         UpdateLine;
  1462.     end;
  1463. end;
  1464. {-- TMMAudioLine --------------------------------------------------------}
  1465. function  TMMAudioLine.GetDestLine: TMMComponent1;
  1466. begin
  1467.     Result := FDestLine;
  1468. end;
  1469. {-- TMMAudioLine --------------------------------------------------------}
  1470. procedure TMMAudioLine.Changed;
  1471. var
  1472.     Chg: TMMLineChange;
  1473. begin
  1474.    Chg:= TMMLineChange.Create;
  1475.    try
  1476.       Chg.LineId:= LineId;
  1477.       FObservable.NotifyObservers(Chg);
  1478.    finally
  1479.       Chg.Free;
  1480.    end;
  1481.    DoChange;
  1482. end;
  1483. {-- TMMAudioLine --------------------------------------------------------}
  1484. procedure TMMAudioLine.LineIdChanged;
  1485. var
  1486.     Chg: TMMLineIdChange;
  1487. begin
  1488.    Chg:= TMMLineIdChange.Create;
  1489.    try
  1490.       FObservable.NotifyObservers(Chg);
  1491.    finally
  1492.       Chg.Free;
  1493.    end;
  1494. end;
  1495. {-- TMMAudioLine --------------------------------------------------------}
  1496. procedure TMMAudioLine.DoChange;
  1497. begin
  1498.    if Assigned(FOnChange) then FOnChange(Self);
  1499. end;
  1500. {-- TMMAudioLine --------------------------------------------------------}
  1501. procedure TMMAudioLine.ControlChanged(CtlId: TMMControlId);
  1502. var
  1503.     Chg: TMMControlChange;
  1504. begin
  1505.    Chg:= TMMControlChange.Create;
  1506.    try
  1507.       Chg.LineId     := LineId;
  1508.       Chg.ControlId  := CtlId;
  1509.       FObservable.NotifyObservers(Chg);
  1510.    finally
  1511.       Chg.Free;
  1512.    end;
  1513.    DoControlChange(CtlId);
  1514. end;
  1515. {-- TMMAudioLine --------------------------------------------------------}
  1516. procedure TMMAudioLine.DoControlChange(CtlId: TMMControlId);
  1517. begin
  1518.    if Assigned(FOnControlChange) then FOnControlChange(Self, FLineId, CtlId);
  1519. end;
  1520. {-- TMMAudioLine --------------------------------------------------------}
  1521. procedure TMMAudioLine.AddObserver(O: TMMObserver);
  1522. begin
  1523.    FObservable.AddObserver(O);
  1524. end;
  1525. {-- TMMAudioLine --------------------------------------------------------}
  1526. procedure TMMAudioLine.RemoveObserver(O: TMMObserver);
  1527. begin
  1528.    if FObservable <> nil then
  1529.       FObservable.RemoveObserver(O);
  1530. end;
  1531. {-- TMMAudioLine --------------------------------------------------------}
  1532. function  TMMAudioLine.GetLineInfoForSource(Source: TMMAudioLine; var Info : TMixerLine): Boolean;
  1533. var
  1534.    i : Integer;
  1535.    Ok: Boolean;
  1536.    function CompTypeOk: Boolean;
  1537.    begin
  1538.     Result := APIToCompType(Info.dwComponentType) = Source.FComponentType;
  1539.    end;
  1540.    function TargetOk: Boolean;
  1541.    begin
  1542.     if Source.Target <> nil then
  1543.        with Source.Target.DeviceCaps do
  1544.        begin
  1545.           Result := (Info.Target.dwType = DeviceTypeToTarget(Source.Target.GetDeviceType)) and
  1546.                     (Info.Target.wMid = ManufacturerId) and
  1547.                     (Info.Target.wPid = ProductId) and
  1548.                     (Info.Target.vDriverVersion = MakeVersion(VerMajor,VerMinor)) and
  1549.                     (StrComp(Info.Target.szPName, PChar(ProductName)) = 0);
  1550.         end
  1551.     else
  1552.         Result := False;
  1553.    end;
  1554. begin
  1555.    Result := False;
  1556.    Ok     := False;
  1557.    if csLoading in ComponentState then
  1558.       Exit;
  1559.    if ValidMixer and
  1560.       (LineId <> badLineId) and
  1561.       not (cfSource in LineInfo.Flags) then
  1562.    begin
  1563.       case Source.FLineSetup of
  1564.         lsLineId   : Ok:= (Source.FLineId <> badLineId) and Mixer.GetLineInfoById(Source.FLineId, Info);
  1565.         lsCompType : Ok:= Mixer.GetLineInfoByCompType(Source.FComponentType, Info);
  1566.         lsTarget   : Ok:= Mixer.GetLineInfoByTarget(Source.FTarget, Info);
  1567.       end;
  1568.       if not Ok then
  1569.         Exit;
  1570.       if Info.dwDestination = LineId then
  1571.       begin
  1572.         Result := True;
  1573.         Exit;
  1574.       end;
  1575.       if Source.FLineSetup = lsLineId then
  1576.         Exit;
  1577.       for i := 0 to Connections - 1 do
  1578.         if Mixer.GetLineInfoBySource(FDestinationId, i, Info) then
  1579.             if ((Source.FLineSetup = lsCompType) and CompTypeOk) or
  1580.                ((Source.FLineSetup = lsTarget) and TargetOk) then begin
  1581.                 Result := True;
  1582.                 Exit;
  1583.             end;
  1584.    end;
  1585. end;
  1586. {== TMMControlInfo ======================================================}
  1587. procedure TMMControlInfo.SetDummyStr(const Value: string);
  1588. begin
  1589.   ;
  1590. end;
  1591. {-- TMMControlInfo ------------------------------------------------------}
  1592. procedure TMMControlInfo.Clear;
  1593. begin
  1594.    FFlags         := [];
  1595.    FMultipleItems := 0;
  1596.    FShortName     := '';
  1597.    FName          := '';
  1598.    FMinValue      := 0;
  1599.    FMaxValue      := 0;
  1600.    FSteps         := 0;
  1601. end;
  1602. {== TMMCustomMixerControl ===============================================}
  1603. constructor TMMCustomMixerControl.Create(AOwner: TComponent);
  1604. begin
  1605.    inherited Create(AOwner);
  1606.    FControlInfo       := TMMControlInfo.Create;
  1607.    FControlId         := badControlId;
  1608.    FObserver          := TMMObserver.Create;
  1609.    FObserver.OnNotify := LineNotify;
  1610.    FObservable        := TMMObservable.Create;
  1611. end;
  1612. {-- TMMCustomMixerControl -----------------------------------------------}
  1613. destructor TMMCustomMixerControl.Destroy;
  1614. begin
  1615.    FObserver.Free;
  1616.    FControlInfo.Free;
  1617.    FObservable.Free;
  1618.    FObservable:= nil;
  1619.    inherited Destroy;
  1620. end;
  1621. {-- TMMCustomMixerControl -----------------------------------------------}
  1622. procedure TMMCustomMixerControl.AddObserver(O: TMMObserver);
  1623. begin
  1624.    FObservable.AddObserver(O);
  1625. end;
  1626. {-- TMMCustomMixerControl -----------------------------------------------}
  1627. procedure TMMCustomMixerControl.RemoveObserver(O: TMMObserver);
  1628. begin
  1629.    if FObservable <> nil then
  1630.       FObservable.RemoveObserver(O);
  1631. end;
  1632. {-- TMMCustomMixerControl -----------------------------------------------}
  1633. procedure TMMCustomMixerControl.CalcParams(C: TMMChannel; Item: TMMItemIndex; var Ch: TMMChannelIndex; var It: TMMItemIndex);
  1634. begin
  1635.    NeedId;
  1636.    if C = chBoth then
  1637.       Ch:= Min(Channels,1)
  1638.    else
  1639.       Ch:= Channels;
  1640.    if Item = NoItem then
  1641.       It:= 0
  1642.    else if InRange(Item,0,Items-1) then
  1643.       It:= Items
  1644.    else
  1645.       { TODO: Should be resource id }
  1646.       raise EMMMixerControlError.Create('Item index out of range');
  1647. end;
  1648. {-- TMMCustomMixerControl -----------------------------------------------}
  1649. function TMMCustomMixerControl.GetChannelSigned(C: TMMChannel; Item: TMMItemIndex): Integer;
  1650. var
  1651.     P  : PIntArray;
  1652.     Size: Integer;
  1653.     Ch : TMMChannelIndex;
  1654.     It : TMMItemIndex;
  1655. begin
  1656.    CalcParams(C,Item,Ch,It);
  1657.    Size:= SizeOf(P^)*Max(Ch,1)*Max(It,1);
  1658.    GetMem(P,Size);
  1659.    try
  1660.       Mixer.GetSignedControl(ControlId,Ch,It,@P^[0]);
  1661.       Result:= P^[ValueIndex(C,Item)];
  1662.    finally
  1663.       FreeMem(P,Size);
  1664.    end;
  1665. end;
  1666. {-- TMMCustomMixerControl -----------------------------------------------}
  1667. procedure TMMCustomMixerControl.SetChannelSigned(C: TMMChannel; Item: TMMItemIndex; Value: Integer);
  1668. var
  1669.     P  : PIntArray;
  1670.     Size: Integer;
  1671.     Ch : TMMChannelIndex;
  1672.     It : TMMItemIndex;
  1673. begin
  1674.    CalcParams(C,Item,Ch,It);
  1675.    Size:= SizeOf(P^)*Max(Ch,1)*Max(It,1);
  1676.    GetMem(P,Size);
  1677.    try
  1678.       Mixer.GetSignedControl(ControlId,Ch,It,@P^[0]);
  1679.       P^[ValueIndex(C,Item)]:= Value;
  1680.       Mixer.SetSignedControl(ControlId,Ch,It,@P^[0]);
  1681.    finally
  1682.       FreeMem(P,Size);
  1683.    end;
  1684. end;
  1685. {-- TMMCustomMixerControl -----------------------------------------------}
  1686. function TMMCustomMixerControl.GetChannelUnsigned(C: TMMChannel; Item: TMMItemIndex): Cardinal;
  1687. var
  1688.     P  : PCardinalArray;
  1689.     Size: Integer;
  1690.     Ch : TMMChannelIndex;
  1691.     It : TMMItemIndex;
  1692. begin
  1693.    CalcParams(C,Item,Ch,It);
  1694.    Size:= SizeOf(P^)*Max(Ch,1)*Max(It,1);
  1695.    GetMem(P,Size);
  1696.    try
  1697.       Mixer.GetUnsignedControl(ControlId,Ch,It,@P^[0]);
  1698.       Result:= P^[ValueIndex(C,Item)];
  1699.    finally
  1700.       FreeMem(P,Size);
  1701.    end;
  1702. end;
  1703. {-- TMMCustomMixerControl -----------------------------------------------}
  1704. procedure TMMCustomMixerControl.SetChannelUnsigned(C: TMMChannel; Item: TMMItemIndex; Value: Cardinal);
  1705. var
  1706.     P  : PCardinalArray;
  1707.     Size: Integer;
  1708.     Ch : TMMChannelIndex;
  1709.     It : TMMItemIndex;
  1710. begin
  1711.    CalcParams(C,Item,Ch,It);
  1712.    Size:= SizeOf(P^)*Max(Ch,1)*Max(It,1);
  1713.    GetMem(P,Size);
  1714.    try
  1715.       Mixer.GetUnsignedControl(ControlId,Ch,It,@P^[0]);
  1716.       P^[ValueIndex(C,Item)]:= Value;
  1717.       Mixer.SetUnsignedControl(ControlId,Ch,It,@P^[0]);
  1718.    finally
  1719.       FreeMem(P,Size);
  1720.    end;
  1721. end;
  1722. {-- TMMCustomMixerControl -----------------------------------------------}
  1723. function TMMCustomMixerControl.GetChannelBoolean(C: TMMChannel; Item: TMMItemIndex): Boolean;
  1724. var
  1725.     P  : PBooleanArray;
  1726.     Size: Integer;
  1727.     Ch : TMMChannelIndex;
  1728.     It : TMMItemIndex;
  1729. begin
  1730.    CalcParams(C,Item,Ch,It);
  1731.    Size:= SizeOf(P^)*Max(Ch,1)*Max(It,1);
  1732.    GetMem(P,Size);
  1733.    try
  1734.       Mixer.GetBooleanControl(ControlId,Ch,It,@P^[0]);
  1735.       Result:= P^[ValueIndex(C,Item)];
  1736.    finally
  1737.       FreeMem(P,Size);
  1738.    end;
  1739. end;
  1740. {-- TMMCustomMixerControl -----------------------------------------------}
  1741. procedure TMMCustomMixerControl.SetChannelBoolean(C: TMMChannel; Item: TMMItemIndex; Value: Boolean);
  1742. var
  1743.     P  : PBooleanArray;
  1744.     Size: Integer;
  1745.     Ch : TMMChannelIndex;
  1746.     It : TMMItemIndex;
  1747.     i  : TMMItemIndex;
  1748. begin
  1749.    CalcParams(C,Item,Ch,It);
  1750.    Size:= SizeOf(P^)*Max(Ch,1)*Max(It,1);
  1751.    GetMem(P,Size);
  1752.    try
  1753.       Mixer.GetBooleanControl(ControlId,Ch,It,@P^[0]);
  1754.       { If controls is single select, then we should clear other items first }
  1755.       if (Items > 0) and IsControlTypeSingleSelect(ControlTypeToAPI(ControlType)) then
  1756.         for i := 0 to Items - 1 do
  1757.             P^[ValueIndex(C,i)] := False;
  1758.       P^[ValueIndex(C,Item)]:= Value;
  1759.       Mixer.SetBooleanControl(ControlId,Ch,It,@P^[0]);
  1760.    finally
  1761.       FreeMem(P,Size);
  1762.    end;
  1763. end;
  1764. {-- TMMCustomMixerControl -----------------------------------------------}
  1765. function    TMMCustomMixerControl.GetItemInfo(Index: TMMItemIndex): TMMItemInfo;
  1766. var
  1767.     P, R   : PMixerControlDetailsListText;
  1768.     Size   : Integer;
  1769. begin
  1770.    NeedId;
  1771.    if not (cfMultiple in ControlInfo.Flags) then
  1772.       { TODO: Should be resource id }
  1773.       raise EMMMixerControlError.Create('Item info requested from non-multiple control');
  1774.    FillChar(Result,SizeOf(Result),0);
  1775.    if not InRange(Index,0,Items-1) then
  1776.       { TODO: Should be resource id }
  1777.       raise EMMMixerControlError.Create('Item index out of range');
  1778.    Size:= SizeOf(P^) * Max(Items,1) * Max(Channels,1);
  1779.    GetMem(P,Size);
  1780.    try
  1781.       Mixer.GetItemsInfo(ControlId,Channels,Items,P);
  1782.       R:= P;
  1783.       Inc(R,Index*Max(Channels,1));
  1784.       with R^ do
  1785.       begin
  1786.          Result.Val1:= dwParam1;
  1787.          Result.Val2:= dwParam2;
  1788.          Result.Name:= szName;
  1789.       end;
  1790.    finally
  1791.       FreeMem(P,Size);
  1792.    end;
  1793. end;
  1794. {-- TMMCustomMixerControl -----------------------------------------------}
  1795. procedure TMMCustomMixerControl.UpdateControl;
  1796. var
  1797.     Info: TMixerControl;
  1798.     Ok  : Boolean;
  1799. begin
  1800.    if csLoading in ComponentState then
  1801.    begin
  1802.       FControlClass:= ControlClassOfType(ControlTypeToAPI(FControlType));
  1803.       Exit;
  1804.    end;
  1805.    Ok:= False;
  1806.    if ValidMixer then
  1807.    begin
  1808.       case FControlSetup of
  1809.         csControlId    : Ok:= Mixer.GetControlInfoById(FControlId, Info);
  1810.         csControlType  : Ok:= Mixer.GetControlInfoByType(LineId, FControlType, Info);
  1811.       end;
  1812.       if Ok and ValidControl(Info) then
  1813.       begin
  1814.          FControlId := Info.dwControlId;
  1815.          FControlType:= APIToControlType(Info.dwControlType);
  1816.          with FControlInfo do
  1817.          begin
  1818.             FFlags         := APIToControlFlags(Info.fdwControl);
  1819.             FMultipleItems := Info.cMultipleItems;
  1820.             FShortName     := Info.szShortName;
  1821.             FName          := Info.szName;
  1822.             FMinValue      := Info.Bounds.lMinimum;
  1823.             FMaxValue      := Info.Bounds.lMaximum;
  1824.             FSteps         := Info.Metrics.cSteps;
  1825.          end;
  1826.       end
  1827.       else
  1828.       begin
  1829.          FControlId := badControlId;
  1830.          FControlInfo.Clear;
  1831.       end;
  1832.     end;
  1833.     FControlClass:= ControlClassOfType(ControlTypeToAPI(FControlType));
  1834.     ControlIdChanged;
  1835. end;
  1836. {-- TMMCustomMixerControl -----------------------------------------------}
  1837. procedure TMMCustomMixerControl.Loaded;
  1838. begin
  1839.    inherited Loaded;
  1840.    UpdateControl;
  1841. end;
  1842. {-- TMMCustomMixerControl -----------------------------------------------}
  1843. procedure TMMCustomMixerControl.LineNotify(Sender, Data: TObject);
  1844. begin
  1845.    if Data is TMMControlChange then
  1846.    begin
  1847.       with (Data as TMMControlChange) do
  1848.       if (LineId = Self.LineId) and (ControlId = Self.ControlId) then
  1849.           Self.Changed;
  1850.    end
  1851.    else if Data is TMMLineIdChange then
  1852.            UpdateControl;
  1853. end;
  1854. {-- TMMCustomMixerControl -----------------------------------------------}
  1855. procedure TMMCustomMixerControl.Notification(AComponent: TComponent; Operation: TOperation);
  1856. begin
  1857.    inherited Notification(AComponent,Operation);
  1858.    if Operation = opRemove then
  1859.    begin
  1860.       if AComponent = AudioLine then
  1861.          AudioLine:= nil;
  1862.    end;
  1863. end;
  1864. {-- TMMCustomMixerControl -----------------------------------------------}
  1865. procedure TMMCustomMixerControl.SetAudioLine(Value: TMMAudioLine);
  1866. begin
  1867.    if Value <> FAudioLine then
  1868.    begin
  1869.       if FAudioLine <> nil then
  1870.          FAudioLine.RemoveObserver(FObserver);
  1871.       FAudioLine:= Value;
  1872.       if FAudioLine <> nil then
  1873.       begin
  1874.          FAudioLine.AddObserver(FObserver);
  1875.          FAudioLine.FreeNotification(Self);
  1876.       end;
  1877.       UpdateControl;
  1878.    end;
  1879. end;
  1880. {-- TMMCustomMixerControl -----------------------------------------------}
  1881. procedure TMMCustomMixerControl.SetControlId(Value: TMMControlId);
  1882. begin
  1883.    if Value <> FControlId then
  1884.    begin
  1885.       FControlId     := Value;
  1886.       FControlSetup  := csControlId;
  1887.       UpdateControl;
  1888.    end;
  1889. end;
  1890. {-- TMMCustomMixerControl -----------------------------------------------}
  1891. procedure TMMCustomMixerControl.SetControlType(Value: TMMControlType);
  1892. begin
  1893.    if Value <> FControlType then
  1894.    begin
  1895.       FControlType   := Value;
  1896.       FControlSetup  := csControlType;
  1897.       UpdateControl;
  1898.    end;
  1899. end;
  1900. {-- TMMCustomMixerControl -----------------------------------------------}
  1901. function TMMCustomMixerControl.GetMixer: TMMMixerDevice;
  1902. begin
  1903.    if (FAudioLine <> nil) and FAudioLine.ValidMixer then
  1904.        Result := FAudioLine.Mixer
  1905.    else
  1906.        Result := nil;
  1907. end;
  1908. {-- TMMCustomMixerControl -----------------------------------------------}
  1909. procedure TMMCustomMixerControl.NeedMixer;
  1910. begin
  1911.    if Mixer = nil then
  1912.       { TODO: Should be resouce id }
  1913.       raise EMMDeviceError.Create('Mixer required for this operation');
  1914. end;
  1915. {-- TMMCustomMixerControl -----------------------------------------------}
  1916. procedure TMMCustomMixerControl.NeedId;
  1917. begin
  1918.    NeedMixer;
  1919.    if FControlId = badControlId then
  1920.       { TODO: Should be resouce id }
  1921.       raise EMMDeviceError.Create('Control not valid');
  1922. end;
  1923. {-- TMMCustomMixerControl -----------------------------------------------}
  1924. function TMMCustomMixerControl.GetLineId: TMMLineId;
  1925. begin
  1926.    NeedMixer;
  1927.    Result := FAudioLine.LineId;
  1928. end;
  1929. {-- TMMCustomMixerControl -----------------------------------------------}
  1930. procedure TMMCustomMixerControl.Changed;
  1931. var
  1932.     CtlChg: TMMControlChange;
  1933. begin
  1934.    CtlChg:= TMMControlChange.Create;
  1935.    try
  1936.       if ValidMixer then
  1937.          CtlChg.LineId      := LineId
  1938.       else
  1939.          CtlChg.LineId      := badLineId;
  1940.       CtlChg.ControlId   := ControlId;
  1941.       FObservable.NotifyObservers(CtlChg);
  1942.    finally
  1943.       CtlChg.Free;
  1944.    end;
  1945.    DoChange;
  1946. end;
  1947. {-- TMMCustomMixerControl -----------------------------------------------}
  1948. procedure TMMCustomMixerControl.ControlIdChanged;
  1949. var
  1950.     CtlChg: TMMControlIdChange;
  1951. begin
  1952.    CtlChg:= TMMControlIdChange.Create;
  1953.    try
  1954.       FObservable.NotifyObservers(CtlChg);
  1955.    finally
  1956.       CtlChg.Free;
  1957.    end;
  1958. end;
  1959. {-- TMMCustomMixerControl -----------------------------------------------}
  1960. procedure TMMCustomMixerControl.DoChange;
  1961. begin
  1962.    if System.Assigned(FOnChange) then
  1963.       FOnChange(Self);
  1964. end;
  1965. {-- TMMCustomMixerControl -----------------------------------------------}
  1966. function TMMCustomMixerControl.StoreControlId: Boolean;
  1967. begin
  1968.    Result:= (FControlSetup = csControlId) and (FControlId <> badLineId);
  1969. end;
  1970. {-- TMMCustomMixerControl -----------------------------------------------}
  1971. procedure TMMCustomMixerControl.SetControlInfo(const Value: TMMControlInfo);
  1972. begin
  1973.   ;
  1974. end;
  1975. {-- TMMCustomMixerControl -----------------------------------------------}
  1976. function TMMCustomMixerControl.GetBoolean: Boolean;
  1977. begin
  1978.    NeedId;
  1979.    Mixer.GetBooleanControl(ControlId, Min(Channels,1), 0, @Result);
  1980. end;
  1981. {-- TMMCustomMixerControl -----------------------------------------------}
  1982. procedure TMMCustomMixerControl.SetBoolean(Value: Boolean);
  1983. begin
  1984.    NeedId;
  1985.    Mixer.SetBooleanControl(ControlId, Min(Channels,1), 0, @Value);
  1986. end;
  1987. {-- TMMCustomMixerControl -----------------------------------------------}
  1988. function TMMCustomMixerControl.GetUnsigned: Cardinal;
  1989. begin
  1990.    NeedId;
  1991.    Mixer.GetUnsignedControl(ControlId, Min(Channels,1), 0, @Result);
  1992. end;
  1993. {-- TMMCustomMixerControl -----------------------------------------------}
  1994. procedure TMMCustomMixerControl.SetUnsigned(Value: Cardinal);
  1995. begin
  1996.    NeedId;
  1997.    Mixer.SetUnsignedControl(ControlId, Min(Channels,1), 0, @Value);
  1998. end;
  1999. {-- TMMCustomMixerControl -----------------------------------------------}
  2000. function TMMCustomMixerControl.GetSigned: Integer;
  2001. begin
  2002.    NeedId;
  2003.    Mixer.GetSignedControl(ControlId, Min(Channels,1), 0, @Result);
  2004. end;
  2005. {-- TMMCustomMixerControl -----------------------------------------------}
  2006. procedure TMMCustomMixerControl.SetSigned(Value: Integer);
  2007. begin
  2008.    NeedId;
  2009.    Mixer.SetSignedControl(ControlId, Min(Channels,1), 0, @Value);
  2010. end;
  2011. {-- TMMCustomMixerControl -----------------------------------------------}
  2012. function TMMCustomMixerControl.GetAvailable: Boolean;
  2013. begin
  2014.    Result:= ValidMixer and (ControlId <> badControlId);
  2015. end;
  2016. {-- TMMCustomMixerControl -----------------------------------------------}
  2017. function TMMCustomMixerControl.GetChannels: Integer;
  2018. begin
  2019.    if Mixer = nil then
  2020.       Result:= 0
  2021.    else if cfUniform in ControlInfo.Flags then
  2022.       Result:= 1
  2023.    else
  2024.       Result:= AudioLine.LineInfo.Channels;
  2025. end;
  2026. {-- TMMCustomMixerControl -----------------------------------------------}
  2027. function TMMCustomMixerControl.GetItems: TMMItemIndex;
  2028. begin
  2029.    if Mixer = nil then
  2030.       Result:= 0
  2031.    else
  2032.       Result:= ControlInfo.MultipleItems;
  2033. end;
  2034. {-- TMMCustomMixerControl -----------------------------------------------}
  2035. function TMMCustomMixerControl.ValueIndex(C: TMMChannel; Item: TMMItemIndex): Integer;
  2036. begin
  2037.    if (Channels < 2) or (C = chBoth) then
  2038.        Result:= Max(Item,0) + 0
  2039.    else if (C = chLeft) then
  2040.        Result:= Max(Channels,1) * Max(Item,0) + 0
  2041.    else
  2042.        Result:= Max(Channels,1) * Max(Item,0) + 1;
  2043. end;
  2044. {-- TMMCustomMixerControl -----------------------------------------------}
  2045. function TMMCustomMixerControl.ValidMixer : Boolean;
  2046. begin
  2047.    Result:= (Mixer <> nil) and (FAudioLine.LineId <> badLineId);
  2048. end;
  2049. {-- TMMCustomMixerControl -----------------------------------------------}
  2050. function TMMCustomMixerControl.CanModify  : Boolean;
  2051. begin
  2052.    Result:= Available and not (cfDisabled in ControlInfo.Flags);
  2053. end;
  2054. {-- TMMCustomMixerControl -----------------------------------------------}
  2055. function TMMCustomMixerControl.GetItemForLine(Line: TMMAudioLine): TMMItemIndex;
  2056. var
  2057.     i : TMMItemIndex;
  2058. begin
  2059.     if (Line <> nil) and (Line.LineId <> badLineId) and
  2060.        Available and (cfMultiple in ControlInfo.Flags) and
  2061.        (ControlType in [ctMixer,ctMux]) then
  2062.         for i := 0 to Items - 1 do
  2063.             if ItemInfo[i].Val1 = Line.LineId then
  2064.             begin
  2065.                 Result := i;
  2066.                 Exit;
  2067.             end;
  2068.     Result := NoItem;
  2069. end;
  2070. {-- TMMCustomMixerControl -----------------------------------------------}
  2071. function TMMCustomMixerControl.ValidControl(const Info: TMixerControl): Boolean;
  2072. begin
  2073.    Result:= True;
  2074. end;
  2075. {-- TMMCustomMixerControl -----------------------------------------------}
  2076. function TMMCustomMixerControl.GetControlSetup: TMMControlSetup;
  2077. begin
  2078.    Result:= FControlSetup;
  2079. end;
  2080. {-- TMMCustomMixerControl -----------------------------------------------}
  2081. function TMMCustomMixerControl.StoreControlType: Boolean;
  2082. begin
  2083.    Result:= (GetControlSetup = csControlType) and (ControlType <> Low(TMMControlType));
  2084. end;
  2085. {== TMMVolumeControl ====================================================}
  2086. constructor TMMVolumeControl.Create(AOwner: TComponent);
  2087. begin
  2088.    inherited Create(AOwner);
  2089.    ControlType:= ctVolume;
  2090. end;
  2091. {-- TMMVolumeControl ----------------------------------------------------}
  2092. procedure TMMVolumeControl.SetUnsigned(Value: Cardinal);
  2093. begin
  2094.    SetChannelUnsigned(chLeft,NoItem,Trunc(Value*Pan(chLeft)));
  2095.    SetChannelUnsigned(chRight,NoItem,Trunc(Value*Pan(chRight)));
  2096. end;
  2097. {-- TMMVolumeControl ----------------------------------------------------}
  2098. procedure TMMVolumeControl.SetSigned(Value: Integer);
  2099. begin
  2100.    SetChannelSigned(chLeft,NoItem,Trunc(Value*Pan(chLeft)));
  2101.    SetChannelSigned(chRight,NoItem,Trunc(Value*Pan(chRight)));
  2102. end;
  2103. {-- TMMVolumeControl ----------------------------------------------------}
  2104. procedure TMMVolumeControl.SetChannelSigned(C: TMMChannel; Item: TMMItemIndex; Value: Integer);
  2105. begin
  2106.    if C = chBoth then
  2107.    begin
  2108.       inherited SetChannelSigned(chLeft,Item,Trunc(Value*Pan(chLeft)));
  2109.       inherited SetChannelSigned(chRight,Item,Trunc(Value*Pan(chRight)));
  2110.    end
  2111.    else inherited SetChannelSigned(C,Item,Value);
  2112. end;
  2113. {-- TMMVolumeControl ----------------------------------------------------}
  2114. procedure TMMVolumeControl.SetChannelUnsigned(C: TMMChannel; Item: TMMItemIndex; Value: Cardinal);
  2115. begin
  2116.    if C = chBoth then
  2117.    begin
  2118.       inherited SetChannelUnsigned(chLeft,Item,Trunc(Value*Pan(chLeft)));
  2119.       inherited SetChannelUnsigned(chRight,Item,Trunc(Value*Pan(chRight)));
  2120.    end
  2121.    else inherited SetChannelUnsigned(C,Item,Value);
  2122. end;
  2123. {-- TMMVolumeControl ----------------------------------------------------}
  2124. function TMMVolumeControl.Pan(C:TMMChannel):Extended;
  2125. begin
  2126.    if ((FPanValue <= 0) and (C = chLeft)) or
  2127.       ((FPanValue >= 0) and (C = chRight)) then
  2128.         Result:= 1
  2129.    else if C = chLeft then
  2130.         Result:= (MaxRightPan - FPanValue) / MaxRightPan
  2131.    else
  2132.         Result:= (MaxLeftPan - FPanValue) / MaxLeftPan;
  2133. end;
  2134. {-- TMMVolumeControl ----------------------------------------------------}
  2135. procedure TMMVolumeControl.SetPanValue(Value: Integer);
  2136. begin
  2137.    if FPanValue <> Value then
  2138.    begin
  2139.       FPanValue:= Value;
  2140.       if Available then
  2141.          AsSigned:= AsSigned;
  2142.    end;
  2143. end;
  2144. {-- TMMVolumeControl ----------------------------------------------------}
  2145. function TMMVolumeControl.ValidControl(const Info: TMixerControl): Boolean;
  2146. begin
  2147.    Result:= Info.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
  2148. end;
  2149. {== TMMPanControl =======================================================}
  2150. constructor TMMPanControl.Create(AOwner: TComponent);
  2151. begin
  2152.    inherited Create(AOwner);
  2153.    ControlType        := ctPan;
  2154.    FSimulate          := True;
  2155.    FSimActive         := False;
  2156.    FObserver          := TMMObserver.Create;
  2157.    FObserver.OnNotify := VolumeNotify;
  2158. end;
  2159. {-- TMMPanControl -------------------------------------------------------}
  2160. destructor TMMPanControl.Destroy;
  2161. begin
  2162.    FObserver.Free;
  2163.    inherited Destroy;
  2164. end;
  2165. {-- TMMPanControl -------------------------------------------------------}
  2166. procedure TMMPanControl.UpdatePan;
  2167. var
  2168.     WasActive: Boolean;
  2169. begin
  2170.    if csLoading in ComponentState then
  2171.       Exit;
  2172.    WasActive := FSimActive;
  2173.    FSimActive:= not inherited GetAvailable and FSimulate and (FVolume <> nil) and
  2174.                 FVolume.Available and not (cfUniform in FVolume.ControlInfo.Flags) and
  2175.                 (FVolume.Channels > 1);
  2176.    if FSimActive then
  2177.    with ControlInfo do
  2178.    begin
  2179.       FFlags         := [cfUniform];
  2180.       FMultipleItems := 0;
  2181.       FShortName     := 'Pan';
  2182.       FName          := 'Simulated pan';
  2183.       FMinValue      := MaxLeftPan;
  2184.       FMaxValue      := MaxRightPan;
  2185.       FSteps         := 0;
  2186.    end;
  2187.    if FSimActive <> WasActive then
  2188.    begin
  2189.       inherited ControlIdChanged;
  2190.       if not WasActive then
  2191.          UpdatePanValue;
  2192.    end;
  2193.    if FVolume <> nil then
  2194.    begin
  2195.       if FSimActive then
  2196.          FVolume.PanValue:= AsSigned
  2197.       else
  2198.          FVolume.PanValue:= 0;
  2199.    end;
  2200. end;
  2201. {-- TMMPanControl -------------------------------------------------------}
  2202. procedure TMMPanControl.UpdateControl;
  2203. begin
  2204.    UpdatePan;
  2205.    if not FSimActive then
  2206.       inherited UpdateControl;
  2207. end;
  2208. {-- TMMPanControl -------------------------------------------------------}
  2209. procedure TMMPanControl.SetControl(Value:TMMVolumeControl);
  2210. begin
  2211.    if Value <> FVolume then
  2212.    begin
  2213.       if FVolume <> nil then
  2214.       begin
  2215.          FVolume.RemoveObserver(FObserver);
  2216.          FVolume.PanValue:= 0;
  2217.       end;
  2218.       FVolume:= Value;
  2219.       if FVolume <> nil then
  2220.       begin
  2221.          FVolume.AddObserver(FObserver);
  2222.          FVolume.FreeNotification(Self);
  2223.       end;
  2224.       UpdateControl;
  2225.    end;
  2226. end;
  2227. {-- TMMPanControl -------------------------------------------------------}
  2228. procedure TMMPanControl.SetSimulate(Value:Boolean);
  2229. begin
  2230.    if FSimulate <> Value then
  2231.    begin
  2232.       FSimulate:= Value;
  2233.       UpdateControl;
  2234.    end;
  2235. end;
  2236. {-- TMMPanControl -------------------------------------------------------}
  2237. procedure TMMPanControl.VolumeNotify(Sender,Data:TObject);
  2238. begin
  2239.    if Data is TMMControlIdChange then
  2240.       UpdateControl
  2241.    else
  2242.       UpdatePanValue;
  2243. end;
  2244. {-- TMMPanControl -------------------------------------------------------}
  2245. procedure TMMPanControl.Notification(AComponent: TComponent; Operation: TOperation);
  2246. begin
  2247.    inherited Notification(AComponent,Operation);
  2248.    if Operation = opRemove then
  2249.       if AComponent = FVolume then
  2250.          VolumeControl:= nil;
  2251. end;
  2252. {-- TMMPanControl -------------------------------------------------------}
  2253. procedure TMMPanControl.UpdatePanValue;
  2254. var
  2255.     Left, Right: Integer;
  2256. begin
  2257.    if FSimActive then
  2258.    begin
  2259.       Left  := VolumeControl.GetChannelSigned(chLeft,NoItem);
  2260.       Right := VolumeControl.GetChannelSigned(chRight,NoItem);
  2261.       if Left = 0 then
  2262.          if Right = 0 then
  2263.          else
  2264.             FValue:= MaxRightPan
  2265.       else
  2266.          if Right = 0 then
  2267.             FValue:= MaxLeftPan
  2268.          else
  2269.             FValue:= Trunc(MaxRightPan * (Right - Left) / Max(Right,Left));
  2270.       { Set directly because setting to prop causes updates of volume controls }
  2271.       VolumeControl.FPanValue:= FValue;
  2272.       Changed;
  2273.    end;
  2274. end;
  2275. {-- TMMPanControl -------------------------------------------------------}
  2276. procedure TMMPanControl.SetSigned(Value: Integer);
  2277. begin
  2278.    if FSimActive then
  2279.    begin
  2280.       FValue:= Value;
  2281.       UpdateVolumePan;
  2282.    end
  2283.    else inherited SetSigned(Value);
  2284. end;
  2285. {-- TMMPanControl -------------------------------------------------------}
  2286. function TMMPanControl.GetSigned: Integer;
  2287. begin
  2288.    if FSimActive then
  2289.       Result:= FValue
  2290.    else
  2291.       Result:= inherited GetSigned;
  2292. end;
  2293. {-- TMMPanControl -------------------------------------------------------}
  2294. function TMMPanControl.GetChannelSigned(C: TMMChannel; Item: TMMItemIndex): Integer;
  2295. begin
  2296.    if FSimActive then
  2297.       Result:= FValue
  2298.    else
  2299.       Result:= inherited GetChannelSigned(C,Item);
  2300. end;
  2301. {-- TMMPanControl -------------------------------------------------------}
  2302. procedure TMMPanControl.SetChannelSigned(C: TMMChannel; Item: TMMItemIndex; Value: Integer);
  2303. begin
  2304.    if FSimActive then
  2305.    begin
  2306.       FValue:= Value;
  2307.       UpdateVolumePan;
  2308.    end
  2309.    else inherited SetChannelSigned(C,Item,Value);
  2310. end;
  2311. {-- TMMPanControl -------------------------------------------------------}
  2312. procedure TMMPanControl.UpdateVolumePan;
  2313. begin
  2314.    if FSimActive then
  2315.       FVolume.PanValue:= FValue;
  2316. end;
  2317. {-- TMMPanControl -------------------------------------------------------}
  2318. function TMMPanControl.GetAvailable: Boolean;
  2319. begin
  2320.    if FSimActive then
  2321.       Result:= True
  2322.    else
  2323.       Result:= inherited GetAvailable;
  2324. end;
  2325. {-- TMMPanControl -------------------------------------------------------}
  2326. function TMMPanControl.ValidControl(const Info: TMixerControl): Boolean;
  2327. begin
  2328.    Result:= Info.dwControlType = MIXERCONTROL_CONTROLTYPE_PAN;
  2329. end;
  2330. {-- TMMPanControl -------------------------------------------------------}
  2331. function TMMPanControl.GetChannels: Integer;
  2332. begin
  2333.    Result:= 1;
  2334. end;
  2335. {-- TMMPanControl -------------------------------------------------------}
  2336. function TMMPanControl.GetItems: TMMItemIndex;
  2337. begin
  2338.    Result:= 0;
  2339. end;
  2340. {== Property service ====================================================}
  2341. function ControlIdToIdent(Id: LongInt; var S: string) : Boolean;
  2342. begin
  2343.     Result := False;
  2344.     if Id = badControlId then
  2345.     begin
  2346.         S := 'badControlId';
  2347.         Result := True;
  2348.     end;
  2349. end;
  2350. {------------------------------------------------------------------------}
  2351. function IdentToControlId(const S: string; var Id: LongInt) : Boolean;
  2352. begin
  2353.     Result := False;
  2354.     if CompareText(S, 'badControlId') = 0 then
  2355.     begin
  2356.         Result  := True;
  2357.         Id      := badControlId;
  2358.     end;
  2359. end;
  2360. {------------------------------------------------------------------------}
  2361. function LineIdToIdent(Id: LongInt; var S: string) : Boolean;
  2362. begin
  2363.     Result := False;
  2364.     if Id = badLineId then
  2365.     begin
  2366.         S := 'badLineId';
  2367.         Result := True;
  2368.     end;
  2369. end;
  2370. {------------------------------------------------------------------------}
  2371. function IdentToLineId(const S: string; var Id: LongInt) : Boolean;
  2372. begin
  2373.     Result := False;
  2374.     if CompareText(S, 'badLineId') = 0 then
  2375.     begin
  2376.         Result  := True;
  2377.         Id      := badLineId;
  2378.     end;
  2379. end;
  2380. {------------------------------------------------------------------------}
  2381. function ItemIndexToIdent(Id: LongInt; var S: string) : Boolean;
  2382. begin
  2383.     Result := False;
  2384.     if Id = NoItem then
  2385.     begin
  2386.         S := 'NoItem';
  2387.         Result := True;
  2388.     end;
  2389. end;
  2390. {------------------------------------------------------------------------}
  2391. function IdentToItemIndex(const S: string; var Id: LongInt) : Boolean;
  2392. begin
  2393.     Result := False;
  2394.     if CompareText(S, 'NoItem') = 0 then
  2395.     begin
  2396.         Result  := True;
  2397.         Id      := NoItem;
  2398.     end;
  2399. end;
  2400. initialization
  2401.     RegisterIntegerConsts(TypeInfo(TMMControlId),IdentToControlId,ControlIdToIdent);
  2402.     RegisterIntegerConsts(TypeInfo(TMMLineId),IdentToLineId,LineIdToIdent);
  2403.     RegisterIntegerConsts(TypeInfo(TMMItemIndex),IdentToItemIndex,ItemIndexToIdent);
  2404. end.