1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
| unit KGF_unit_SpeakerControl;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics,
Controls, Forms, Dialogs, ActiveX, ComObj, MMSystem,
ComCtrls, ExtCtrls,
KGF_unit_data; // uniquement pour la variable MainFormHandle qui y est définie de façon globale
type
EDATAFLOW = TOleEnum;
EROLE = TOleEnum;
IMMDevice = interface(IUnknown)
['{D666063F-1587-4E43-81F1-B948E807363F}']
function Activate(const iid: TGUID; const dwClsCtx: UINT; const pActivationParams: PPropVariant; out ppInterface: IUnknown)
: HRESULT; stdcall;
end;
IMMDeviceCollection = interface(IUnknown)
['{0BD7A1BE-7A1A-44DB-8397-CC5392387B5E}']
end;
IMMDeviceEnumerator = interface(IUnknown)
['{A95664D2-9614-4F35-A746-DE8DB63617E6}']
function EnumAudioEndpoints(const dataFlow: EDATAFLOW; const dwStateMask: DWORD; out ppDevices: IMMDeviceCollection): HRESULT; stdcall;
function GetDefaultAudioEndpoint(const dataFlow: EDATAFLOW; const role: EROLE; out ppEndpoint: IMMDevice): HRESULT; stdcall;
end;
IAudioMeterInformation = interface(IUnknown)
['{C02216F6-8C67-4B5B-9D00-D008E73E0064}']
function GetPeakValue(out pfPeak: Single): HRESULT; stdcall;
function GetMeteringChannelCount(out pnChannelCount: UINT): HRESULT; stdcall;
function GetChannelsPeakValues(u32ChannelCount: UINT; out afPeakValues: pSingle): HRESULT; stdcall;
function QueryHardwareSupport(out pdwHardwareSupportMask: UINT): HRESULT; stdcall;
end;
const
IID_IMMDeviceEnumerator: TGUID = '{A95664D2-9614-4F35-A746-DE8DB63617E6}';
CLASS_IMMDeviceEnumerator: TGUID = '{BCDE0395-E52F-467C-8E3D-C4579291692E}';
IID_IAudioMeterInformation: TGUID = '{C02216F6-8C67-4B5B-9D00-D008E73E0064}';
eRender = $00000000;
eConsole = $00000000;
type TDummyForm = class
class procedure Timer1Timer(Sender: TObject);
end;
var
peak: IAudioMeterInformation = nil;
SpeakerTimer: TTimer;
pVolumeOutputL, pVolumeOutputR, pVolume: pinteger; // <=== utilisé pour retourner dynamiquement les peak values deu volume
MaxOutput: Integer;
device: IMMDevice;
deviceEnumerator: IMMDeviceEnumerator;
nChannels: UINT;
ApplicationTitle: String; // utilisé pour mémoriser le titre de la fenêtre principale
implementation
function InitSpeakerControl(time, MaxV: integer; poutL, poutR, pVol: pinteger):integer; stdcall; export;
var
n: integer;
begin
SpeakerTimer := TTimer.Create(nil);
SpeakerTimer.Enabled := False;
SpeakerTimer.OnTimer := TDummyForm.Timer1Timer;
SpeakerTimer.Interval:= time;
MaxOutput := MaxV;
pVolumeOutputL := poutL;
pVolumeOutputR := poutR;
pVolume := pVol;
CoCreateInstance(CLASS_IMMDeviceEnumerator, nil, CLSCTX_ALL, IID_IMMDeviceEnumerator, deviceEnumerator);
deviceEnumerator.GetDefaultAudioEndpoint(eRender, eConsole, device);
device.Activate(IID_IAudioMeterInformation, CLSCTX_ALL, nil, IUnknown(peak));
peak.GetMeteringChannelCount(nChannels);
SpeakerTimer.Enabled := true;
// les 3 lignes suivantes sont essentielles - le titre de la fenêtre principale doit être réaffiché dans la routine OnTimer, sinon, les valeurs restent à zéro !
n := SendMessage(MainFormHandle,WM_GETTEXTLENGTH,0,0) + 1; // retourner la longeur du titre de la fenêtre principale
SetLength(ApplicationTitle,n); // réserver de l'espace pour le titre
SendMessage(MainFormHandle,WM_GETTEXT,n,integer(@ApplicationTitle[1])); // récupérer le titre de la fenêtre principale
result := 0;
end;
exports InitSpeakerControl;
function CloseSpeakerControl():integer; stdcall; export;
begin
if assigned(SpeakerTimer) then begin
SpeakerTimer.Enabled := false;
SpeakerTimer.Free;
// peak.Free;
end;
result := 0;
end;
exports CloseSpeakerControl;
// Ceci est la fonction évènement OnTimer du timer créé lors de la mise en route de la surveillance du volume
class procedure TDummyForm.Timer1Timer(Sender: TObject);
var
ChannelVolumes: array[1..2] of Single; // ces variables sont prévues pour recevoir les PeakValues des deux canaux, par la méthode GetChannelsPeakValues
pVolumes: pSingle; // ce pointeur doit pointer sur le premier élément du tableau ci-dessus, et est passé en paramètre à GetChannelsPeakValues
Temp: Single; // cette variable reçoit la peak value générale, par la méthode GetPeakValue
vL, vR, vV: integer; // ce sont des variables techniques pour ajuster les valeurs reçues vers l'intervalle imposé [0..MaxOutput]
s: string; // variable temporaire pour afficher le titre - utiliser ici la variable ApplicationTitle NE MARCHE PAS !
HR: HResult; // code retour de la méthode GetChannelsPeakValues
begin
ttimer(sender).Enabled := false; // arrêter le timer, le temps de traiter l'évènement
ChannelVolumes[1] := 0; // assurer que le tableau est bien lis à zéro
ChannelVolumes[2] := 0;
pVolumes := pSingle(@ChannelVolumes[1]); // créer le pointeur vers le début du tableau
// si j'ai bien compris la documentation de MSDN, la méthode GetChannelsPeakValues prens 2 paramètres:
// le premier donne le nombre de canaux (deux dans mon cas, et j'ai vérifié le contenu de la variab
peak.GetPeakValue(Temp); // prendre la valeur instantané du volume global, résultat dans Temp ==> correct !
// +++++++++++++++ la ligne suivante pose problème avec le volume du second canal:
HR := peak.GetChannelsPeakValues(nChannels,pVolumes); // prendre les valeurs instantanées des deux canaux <========== Seul [1] est retourné, [2] reste à zéro !
// un pointeur vers le premier élément d'un tableau de flottants (de type Single)
// la méthode est supposé remplir le tableau avec les deux valeurs...
if nChannels<>2 then showmessage('nChannels='+inttostr(nChannels));
if HR<>S_OK then begin
if HR=E_INVALIDARG then showmessage('E_INVALIDARG')
else if HR=E_POINTER then showmessage('E_POINTER')
else showmessage('Erreur '+inttostr(integer(HR))+' = '+inttohex(integer(HR),8));
end;
// sans les deux lignes suivantes, le contenu du tableau ChannelVolumes reste à zéro, même pour le [1].
// avec ces deux lignes stupides, le [1] est renseigné correctement,, mais le [2] reste toujours à zéro !
s := ApplicationTitle;
SendMessage(MainFormHandle,WM_SETTEXT,0,integer(@s[1]));
// traduire en valeurs entre 0 et 100
vL := Round(ChannelVolumes[1] * MaxOutput); // valeur canal droit <======== valeur correcte !
vR := Round(ChannelVolumes[2] * MaxOutput); // valeur canal droit <======== ici, reste à zéro !
vV := Round(temp * MaxOutput); // valeur canal gauche <====== valeur correcte !
// envoyer ces valeurs dans des variables de l'application, si leur adresse a été passée en paramètre à la fonction InitSpeakerControl
if pVolumeOutputL<>nil then pVolumeOutputL^ := vL;
if pVolumeOutputR<>nil then pVolumeOutputR^ := vR;
if pVolume<>nil then pVolume^ := vV;
ttimer(sender).Enabled := true; // relancer le timer
end;
end. |
Partager