IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

API, COM et SDKs Delphi Discussion :

Trouver les peak values des deux canaux du haut-parleur


Sujet :

API, COM et SDKs Delphi

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 369
    Billets dans le blog
    1
    Par défaut Trouver les peak values des deux canaux du haut-parleur
    Pour une DLL réalisée en Delphi V6 Personal Edition, je suis en train de développer des fonctions pour gérer la sortie haut-parleur du système. J'arrive à lire et positionner le volume général ainsi ue l'indicateur "muet", et le rendre b-directionnel via une procédure call-back, de sorte que la scrollbar du volume de mon application se positionne tout seul lorsqu'on change le volume général du mixeur, etc.

    Par contre, j'ai un problème pour lire la valeur instantanée du volume, sur les deux canaux (channel peak value). Je trouve sans problème la valeur instantanée du volume général, mais le problème se pose en essayant de lire les valeurs instantanées individuelles des canaux.

    Je suis arrivé à créer une unité qui peut lire correctement la valeur instantanée du volume sur le canal gauche, mais celle du canal droit reste fixe à zéro. Pourtant, le nombre de canaux que l'interface retourne, est bien de 2. Et en lançant le VuMeter de SoureForge, je vois bien les deux aiguilles bouger, et donc, les deux canaux travaillent bien.

    Pire encore, je ne trouve la valeur du canal gauche uniquement que si, entre l'API récupérant la valeur et mon code pour la traiter, j'intercale un SendMessage pour réafficher le titre de la fenêtre principale (que j'ai capté et mémorisé à l'initialisation du programme). Si je ne fais pas ça, même la valeur pour le canal gauche reste à zéro ! Je suis tombé sur cette astuce bizarre lorsque, en désespoir de cause, j'avais intercalé un ShowMessage à cet endroit. Et comme par magie, ma valeur du canal gauche apparaissait. Et elle restait en remplaçant le ShowMessage par un SendMessage. Pourquoi ? Problème de timing ?

    Je poste ci-après mon code complet de cette unité. Il comprend 2 fonctions, dont l'étrange format d'appel est imposé par le langage spécifique auquel est destiné cette DLL:
    - une fonction InitSpeakerControl dont les 4 paramètres sont la valeur maximale attendue pour les nveaux de son, ainsi que 3 adresses de variables de type Integer, pour y déposer de façon automatique les niveaux de volume gauche, droite et général.
    - une fonction CloseSpeakerControl sans paramètres qui est supposé faire le ménage et tout libérer (elle n'est pas complète, à l'évidence)

    La fonction InitSpeakerControl lance un timer qui, à chaque itération, va chercher le volume général et celui des deux canaux, va transformer ces valeurs pour aboutir à une mise à l'échelle par rapport à l'intervalle imposé, puis va transmettre ces valeurs au programme appelant en les déposant dans les adresses des 3 variables. C'est un fonctionnement simpliste. Le problème se situe aux endroits marqués dans le code, par des commentaires.

    Voici le source:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    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(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:= 200;
      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;
     
    class procedure TDummyForm.Timer1Timer(Sender: TObject);
    var
      ChannelVolumes: array[1..2] of Single;
      pVolumes: pSingle;
      Temp: Single;
      vL, vR, vV: integer;
      s: string;                                        // variable temporaire pour afficher le titre - utiliser ici la variable ApplicationTitle NE MARCHE PAS !
    begin
      ttimer(sender).Enabled := false;                                // arrêter le timer, le temps de traiter l'évènement
      ChannelVolumes[1] := 0;
      ChannelVolumes[2] := 0;
      pVolumes := pSingle(@ChannelVolumes[1]);
     
      peak.GetPeakValue(Temp);
      peak.GetChannelsPeakValues(nChannels,pVolumes);      // <========== Seul [1] est retourné, [2] reste à zéro !
     
      // 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]));
     
      // produire des valeurs entières entre 0 et 65535
      vL := Round(ChannelVolumes[1] * 65535);
      vR := Round(ChannelVolumes[2] * 65535);
     
      // traduire en valeurs entre 0 et 100
      vL := Round((vL * MaxOutput)/65536);
      vR := Round((vR * MaxOutput)/65536);
      vV := Round(temp * MaxOutput);
     
      // 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.
    J'ai deux questions précises:

    1. pourquoi, sans les 2 lignes
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
      s := ApplicationTitle;
      SendMessage(MainFormHandle,WM_SETTEXT,0,integer(@s[1]));
    j'ai uniquement zéro comme volume, alors qu'avec ces lignes au moins le volume gauche apparaît et est correct ?

    2. pourquoi, avec ou sans ces deux lignes, je ne peux pas avoir le volume du canal droit ?

    Ca fait des jours que je me bats avec ça, et je n'arrive pas à comprendre. Je vous serais très reconnaissant pour un coup de main !

  2. #2
    Expert confirmé
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    11 158
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 11 158
    Par défaut
    Bonjour,

    Q1 : pas la moindre idée

    Q2 : vit' vit' et pas testé, mais au premier coup d'œil rapide, il ne manque pas quelque chose là ? :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class procedure TDummyForm.Timer1Timer(Sender: TObject);
    var
      ChannelVolumes: array[1..2] of Single;
      pVolumes: pSingle;
    begin
      ttimer(sender).Enabled := false;                                // arrêter le timer, le temps de traiter l'évènement
      ChannelVolumes[1] := 0;
      ChannelVolumes[2] := 0;
      pVolumes := pSingle(@ChannelVolumes[1]);
    genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class procedure TDummyForm.Timer1Timer(Sender: TObject);
    var
      ChannelVolumes: array[1..2] of Single;
      pVolumes1: pSingle;
      pVolumes2: pSingle;
    begin
      ttimer(sender).Enabled := false;                                // arrêter le timer, le temps de traiter l'évènement
      ChannelVolumes[1] := 0;
      ChannelVolumes[2] := 0;
      pVolumes1 := pSingle(@ChannelVolumes[1]);
      pVolumes2 := pSingle(@ChannelVolumes[2]);
    Juste une idée comme ça.

  3. #3
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 369
    Billets dans le blog
    1
    Par défaut
    Merci d'avoir pris la peine de regarder, Jipété.

    Mais non, je ne crois pas que quelque chose manque. Je reconnais le le choix des noms de variables n'est pas très judicieux. Je reposte la séquence correspondante, avec plus de commentaires:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    // 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 !
    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 !
      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...
     
      // 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]));
     
      // produire des valeurs entières entre 0 et 65535
      vL := Round(ChannelVolumes[1] * 65535);                 // les valeurs instantanées des canaux sont retournées comme valeur flottante entre [0...1.0]
      vR := Round(ChannelVolumes[2] * 65535);                 // donc, je les transforme en entier de type WORD entre [0...65535]
     
      // traduire en valeurs entre 0 et 100
      vL := Round((vL * MaxOutput)/65536);                    // valeur canal gauche <====== valeur correcte !
      vR := Round((vR * MaxOutput)/65536);                    // 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;
    Si, dans cette routine évènement, je force la valeur de ChannelVolumes[2] par un résultat aléatoire, cette valeur est bien transmise au programme princpal, via le mécanisme de transfert par pointeur en fin de procédure. Ceci n'est pas en cause. Le problème, c'est que ChannelVolumes[2] n'est pas chargé par la méthode. D'ailleurs, si je charge une valeur quelconque dedans, avant l'appel, elle reste après l'appel - elle n'est même pas mise à zéro ! La méthode ne modifie tout simplement pas cette cellule.

  4. #4
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 369
    Billets dans le blog
    1
    Par défaut
    Je me heurte toujours au même problème. Mis à part le fait que, dans mon code, je prends également le volume global, la récupération des volumes par canal conduit toujours au même résultat. J'ai conçu mon code en adaptant un code similaire, aidé par la doc de MSN. Et, en toute logique, si la valeur pour le premier canal arrive, celle pour le second doit arriver également. Car, après tout, il s'agit d'un seul et unique appel de la méthode de l'interface, et non deux appels distincts dont un pourrait être mal paramétré.

  5. #5
    Expert confirmé
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    11 158
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 11 158
    Par défaut
    Bon, ben alors tu devrais tester la valeur de retour de peak.GetMeteringChannelCount(nChannels); histoire qu'il n'y ait pas d'erreurs, et si ok, fais afficher nChannels juste pour être sûr qu'il y en a bien 2...

  6. #6
    Expert confirmé
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    11 158
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 11 158
    Par défaut
    Bonsoir,

    si c'est possible, tu devrais générer un projet de test réduit au strict nécessaire et mettant en évidence le problème, tout ça packagé dans un .zip, afin que d'autres puissent tester.

  7. #7
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 369
    Billets dans le blog
    1
    Par défaut
    Oui... mais non ! Delphi 6 Personal Edition est têtu ! Voici le résultat de compilation, avec cette déclaration:
    Nom : aa1.png
Affichages : 295
Taille : 27,7 Ko
    et la déclaration!:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
      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;    // version 1
    //    function GetChannelsPeakValues(u32ChannelCount: UINT; out afPeakValues): HRESULT; stdcall;    //version 2
        function GetChannelsPeakValues(u32ChannelCount: UINT; afPeakValues: pSingle): HRESULT; stdcall;    //version 3
        function QueryHardwareSupport(out pdwHardwareSupportMask: UINT): HRESULT; stdcall;
      end;

  8. #8
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 938
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 938
    Par défaut
    Etrange alors

    ChannelVolumes est un pointeur sur un array et non un array et la déclaration est un pointeur sur un single et non un single...

    Bref, laisse comme c'était si ça fonctionne ainsi (je ne suis pas au bureau et ne peut pas tester)

  9. #9
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 369
    Billets dans le blog
    1
    Par défaut
    J'ai affiché les erreurs comme suit:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    // 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 !
      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;
    Résultat:
    - pour le nombre de canaux: pas d'affichage - il y en a bien deux
    - pour le code retour de GetChannelsPeakValues: pas d'affichage - le code retour est bien S_OK

    Mystère...

  10. #10
    Expert confirmé
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    11 158
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 11 158
    Par défaut
    Citation Envoyé par KlausGunther Voir le message
    Mystère...
    Jette un œil à cette discussion chez SO, je ne dis pas que c'est la solution, mais tu pourras comparer des trucs et des machins...

    Après, je passe, car ça me dépasse.

  11. #11
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 369
    Billets dans le blog
    1
    Par défaut
    Voilà qui est fait. Un projet complet, opérationnel, réalisé avec Delphi 6 Personal Edition.

    J'y ai inclus les fonctions en question en provenance de ma DLL, sans aucune modification, et je les ai appelées par deux boutons, au lieu de les appeler par n langage externe. Puis, j'ai ajouté un simple timer Delphi nommé Surveillance qui se déclanche toutes les 90 millisecondes. Il va afficher le contenu de mes 3 variables supposées recevoir les PeakVolume du canal gauche et droit ainsi que le PeakVolume global.

    Au démarrage, il ne se passe rien, bien sûr. Il faut cliquer sur "Démarrer" et les 3 valeurs s'affichent. Si le volume général est coupé, les 3 valeurs resten à zéro. Si l'on monte le volume général, il ne se passe toujours rien. Normal. Mais si maintenant un fait jouer un son ou une vidéo quelconque, on voit immédiatement que la canal gauche et le volume général travaillent, mais pas le canal droit.

    Voilà, je ne vois pas ce que je pourrais faire de mieux pour montrer le problème...
    Fichiers attachés Fichiers attachés

  12. #12
    Modérateur
    Avatar de tourlourou
    Homme Profil pro
    Biologiste ; Progr(amateur)
    Inscrit en
    Mars 2005
    Messages
    3 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Biologiste ; Progr(amateur)

    Informations forums :
    Inscription : Mars 2005
    Messages : 3 938
    Billets dans le blog
    6
    Par défaut
    Une idée de test à la c*** (au cas où éléments du tableau pas "rangés" dans le même sens) :
    déclarer le tableau de singles [0..2]
    passer l'adresse de l'élément d'indice 1
    regarder les 3 valeurs après appel de la fonction

    NB: je n'y crois pas, mais en même temps, on n'a pas d'explication satisfaisante du comportement...
    Delphi 5 Pro - Delphi 11.3 Alexandria Community Edition - CodeTyphon 6.90 sous Windows 10 ; CT 6.40 sous Ubuntu 18.04 (VM)
    . Ignorer la FAQ Delphi et les Cours et Tutoriels Delphi nuit gravement à notre code !

  13. #13
    Expert confirmé
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    11 158
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 11 158
    Par défaut
    Citation Envoyé par KlausGunther Voir le message
    Voilà qui est fait. Un projet complet, opérationnel, réalisé avec Delphi 6 Personal Edition.
    Bien joué.
    Plus qu'à attendre les retours des copains...

    En ce qui me concerne, je suis coincé de chez coincé pour la bonne et simple raison que j'ai des vieilles machines Windows (en machines virtuelles) mais c'est du XP et du 2000, or les API's que tu manipules sont notées chez MS "à partir de Vista",
    J'ai quand même testé dans une Seven (mais elle n'a pas d'outils de développement), et le problème est identique : on est donc au moins sûr que le problème est dans ton code et pas dans ta machine, c'est déjà un point pour avancer.

    Voilà pour ma contribution, je regrette de ne pouvoir aller plus loin...

  14. #14
    Membre émérite
    Avatar de Cirec
    Profil pro
    Inscrit en
    Octobre 2010
    Messages
    467
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2010
    Messages : 467
    Par défaut
    Bonjour et bonnes fêtes,

    très bonne idée que celle de créer un projet démo

    je pense avoir trouvé le problème qui se situe au niveau du passage du PSingle à la fonction "peak.GetChannelsPeakValues"
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    begin
      ttimer(sender).Enabled := false;          // arrêter le timer, le temps de traiter l'évènement
      ChannelVolumes[0] := 0;                   // assurer que le tableau est bien lis à zéro
      ChannelVolumes[1] := 0;
    // suppression de cette ligne
    //  pVolumes := pSingle(@ChannelVolumes[0]);  // 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 !
    // pour un passage direct    et là ça fonctionne ;)
      HR := peak.GetChannelsPeakValues(nChannels, pSingle(ChannelVolumes[0]));      // 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...
    voilà en espérant que ça t'aidera

    Cordialement,
    @+

  15. #15
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 369
    Billets dans le blog
    1
    Par défaut
    Ca marche ! Très bien. Le code en est encore plus simple. Je garde cette version.

    Et... je suis bien content que tu ne m'aies pas "fichu la paix" jusqu'à maintenant - c'était utile et productif ! Merci !

  16. #16
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 369
    Billets dans le blog
    1
    Par défaut
    Merci, Andnotor. Je vois enfin plus clair.

    Voici une version plus "lisible", qui passe à la compilation et qui fonctionne en exécution:

    Déclaration de l'interface:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
      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; afPeakValues: pSingle): HRESULT; stdcall;    //version 3
        function QueryHardwareSupport(out pdwHardwareSupportMask: UINT): HRESULT; stdcall;
      end;
    Déclaration des variables globales:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var
      peak: IAudioMeterInformation = nil;
      SpeakerTimer: TTimer;
      ChannelVolumes: array of Single;          // ces variables sont prévues pour recevoir les PeakValues des deux canaux, par la méthode GetChannelsPeakValues
      pChannelVolumes: pSingle;                 // pointeur sur ChannelVolumes
      pVolumeOutputL, pVolumeOutputR, pVolume: pinteger;   // <=== utilisé pour retourner dynamiquement les peak values deu volume
      MaxOutput: Integer;
      device: IMMDevice;
      deviceEnumerator: IMMDeviceEnumerator;
      nChannels: UINT;
    Etablissement de la connexion:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    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);
      SetLength(ChannelVolumes,nChannels);
      pChannelVolumes := @ChannelVolumes[0];   // <========== chargement du pointeur
      SpeakerTimer.Enabled := true;
     
      result := 0;
    end;
    exports InitSpeakerControl;
    Fonction cherchant les PeakVolumes:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // 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
      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]
      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
      peak.GetPeakValue(Temp);                  // prendre la valeur instantané du volume global, résultat dans Temp ==> correct !
      HR := peak.GetChannelsPeakValues(nChannels,pChannelVolumes);              // version 3  opérationnelle
      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;
    ...
    Bon, je vais garder la version comme ça. Elle est claire et lisible, et mieux, elle fonctionne.

    Merci à vous tous pour votre aide. Avec le recul, je pense que l'idée maîtresse était de changer la définition de la méthode de l'interface. Ensuite, n'ayant pas vraiment compris, j'ai mal adapté les paramètres. Maintenant, les choses sont claires.

  17. #17
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 938
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 938
    Par défaut
    Je te propose encore un dernier test pour supprimer pChannelVolumes

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    HR := peak.GetChannelsPeakValues(nChannels, pointer(ChannelVolumes));
    pointer() n'est pas la récupération d'une adresse (comme c'est le cas avec pSingle()) mais la conversion d'un pointeur typé en pointeur quelconque, donc compatible avec tout type de pointeur.

    Après je te fiche la paix

  18. #18
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 369
    Billets dans le blog
    1
    Par défaut
    C'est une idée. Je vais essayer ça.

  19. #19
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 369
    Billets dans le blog
    1
    Par défaut
    Merci, Jipété ! J'avais vu ce lien, et c'est bien là que j'ai pris la récupération du volume général:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
      peak.GetPeakValue(Temp);                  // prendre la valeur instantané du volume global, résultat dans Temp ==> correct !
    et ça marche. Par contre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
      HR := peak.GetChannelsPeakValues(nChannels,pVolumes);      // prendre les valeurs instantanées des deux canaux <========== Seul [1] est retourné, [2] reste à zéro !
    pose problème. soit je n'ai pas compris la doc de MSDN (ce qui est fort possible), soit je l'ai mal appliquée - dans tous les cas, je suis certain que le problème est dans mon code, car, comme je l'ai dit au début, j'avais téléchargé le freeware VuMeter de SourceForge (malheureusement, juste un EXE, pas de sources...), et cet outil montre bien un VuMeter avec les aiguilles des deux canaux qui bougent. Comment fait-il, ce programme ? Mystère...

    En tout cas, merci bien d'avoir cherché. Cela m'a poussé à analyser plus précisément les valeurs en question (en tout cas, le code erreur), mais manque de chance, l'API signale que tout est ok...

  20. #20
    Expert confirmé
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    11 158
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 11 158
    Par défaut
    Salut,

    un dernier mot : tu devrais jeter un œil à la librairie bass.

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 3 123 DernièreDernière

Discussions similaires

  1. trouver les noeuds avec des valeurs nulles
    Par awalter1 dans le forum Général Python
    Réponses: 3
    Dernier message: 28/10/2010, 14h33
  2. Trouver les trous dans des périodes sur la même table
    Par CinePhil dans le forum Requêtes
    Réponses: 5
    Dernier message: 11/04/2009, 08h56
  3. Trouver les fichiers contenant des annotations
    Par lahitsitely78 dans le forum Eclipse Platform
    Réponses: 0
    Dernier message: 18/02/2009, 11h52
  4. Trouver les redirections dans des traces
    Par severine dans le forum Développement
    Réponses: 3
    Dernier message: 21/04/2004, 18h51

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo