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

  1. #1
    Membre habitué

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    294
    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 : 294
    Points : 171
    Points
    171
    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 éminent sénior
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    10 919
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 10 919
    Points : 15 356
    Points
    15 356
    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 habitué

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    294
    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 : 294
    Points : 171
    Points
    171
    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
    Expert éminent sénior
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    10 919
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 10 919
    Points : 15 356
    Points
    15 356
    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...

  5. #5
    Membre habitué

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    294
    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 : 294
    Points : 171
    Points
    171
    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...

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

    Informations forums :
    Inscription : Juillet 2006
    Messages : 10 919
    Points : 15 356
    Points
    15 356
    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.

  7. #7
    Membre habitué

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    294
    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 : 294
    Points : 171
    Points
    171
    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...

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

    Informations forums :
    Inscription : Juillet 2006
    Messages : 10 919
    Points : 15 356
    Points
    15 356
    Par défaut
    Salut,

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

  9. #9
    Membre habitué

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    294
    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 : 294
    Points : 171
    Points
    171
    Billets dans le blog
    1
    Par défaut
    Mouais... j'avais vu cette bibliothèque. C'est un monument, c'est distribué sous forme de DLL, et il y a un wrapper Delphi autour de tout ça. Et c'est gratuit unquement pour des projets freeware...

    Or, tout mon projet qui est une DLL de fonctions d'extension à un langage freeware de type clone de Basic, est de distribuer mon produit de facto en Open Source, sans aucune contrrainte de licence, libre de droit pour tout usage: personnel, associatif, éducatif, commercial, avec le droit de copier et de modifier les sources sans restriction. Oui, je sais, je suis un peu "bizarre", mais c'est ma façon de rendre un peu au net ce qu'il m'a donné...

    Et puis, utiliser une grosse DLL d'une partie tièrce pour faire ce qu'une simple API Windows est supposée faire, c'est un peu tirer sur des moineaux avec des canons. Moi, j'aime bien comprendre, et là, je ne comprends pas où est mon erreur. Car, forcément, l'erreur vient de mon code, puisque d'autres logiciels sont capables de faire ce que je veux faire.

    EDIT

    Je cherche une solution à ce problème depuis plus d'une semaine. J'ai consulté des centaines de pages internet, y compris sur de siites russes, chinois, polonais, brésiliens, que sais-je. Il y a une multitude de sites documentant la récupération du volume général - je l'ai et ça marche bien. Mais il n'y a pratiquement rien concernant cette fichue méthode GetChannelsPeakValues. La seule information sérieuse à ce sujet vient de MSDN, mais c'est en syntaxe C. Je ne parle pas C, et j'ai donc un doute sur ma façon de traduire cela en Delphi. Le problème peut très bien venir de là. Ce qui est troublant, c'est que le nombre de canaux retourné est bien 2, la valeur pour le premier canal (le gauche) est bien retournée au bon endroit et est correcte, mais celle pour le canal 2 (le droit) n'apparaît jamais et reste à zéro, ceci sans signaler d'erreur.

    A toutes fins utiles, je poste ici l'état actuel de l'unité qui contient le code en difficulté:
    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
    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.

  10. #10
    Membre habitué

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    294
    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 : 294
    Points : 171
    Points
    171
    Billets dans le blog
    1
    Par défaut
    Je renouvelle mon appel. Est-ce quelqu'un a une idée pourquoi mon code ne marche pas ? Pourquoi mon code ne revoit-il pas la valeur du volume instantané (peak volume) du canal droit, alors que celui du canal gauche ainsi que le volume instantané général est bien trouvé ? Pourtant, le canal droit fonctionne bien, et le logiciel freeware VuMeter téléchargé chez SourceForge montre bien les deux canaux actifs ? Malheureusement, ce logiciel est distribué en EXE, sans les sources. J'ai essayé de suivre exactement la doc, mais il doit manquer quelque chose...

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

    Informations forums :
    Inscription : Juillet 2006
    Messages : 10 919
    Points : 15 356
    Points
    15 356
    Par défaut
    Salut,

    Dans les "Remarks" de msdn, on peut lire
    Citation Envoyé par msdn
    If the stream contains n channels, the channels are numbered 0 to n– 1.
    Tu devrais donc peut-être tenter
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
      ChannelVolumes: array[0..1] of Single;    // ces variables sont prévues pour recevoir les PeakValues des deux canaux, par la méthode GetChannelsPeakValues
    et surtout
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
      ChannelVolumes[0] := 0;                   // assurer que le tableau est bien lis à zéro
      ChannelVolumes[1] := 0;
      pVolumes := pSingle(@ChannelVolumes[0]);  // créer le pointeur vers le début du tableau
    Parce que si ça se trouve, avec ton code, tu ne lis qu'un seul channel, le second...

  12. #12
    Membre habitué

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    294
    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 : 294
    Points : 171
    Points
    171
    Billets dans le blog
    1
    Par défaut
    Merci d'avoir pris le temps de regarder cela, juste après Noël que je te souhaite heureux et paisible !
    J'ai essayé ce que tu m'as conseillé, mais le résultat est malheureusement identique. Voici ce que ça donne, visuellement:
    Nom : aa1.png
Affichages : 425
Taille : 112,8 Ko
    La série des Vu-meters en haut est l'affichage des valeurs que je capte, avec, de gauche à droite: canal gauche, volume général, canal droit
    et en bas, l'affichage du freeware VuMeter de SourceForge. Eux, ils détectent le canal droit. Pas moi. C'est frustrant...

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

    Informations forums :
    Inscription : Juillet 2006
    Messages : 10 919
    Points : 15 356
    Points
    15 356
    Par défaut
    Salut,
    Citation Envoyé par KlausGunther Voir le message
    Nom : aa1.png
Affichages : 425
Taille : 112,8 Ko
    Rien à voir avec ton problème pour lequel je ne vois pas de solution à cette heure (retourne tout le web, il doit bien y avoir quelqu'un qui donne un exemple, même en C ou en JavaScript ou autre, après on peut toujours traduire), par contre, comme tout sert à tout dans la vie, juste un mot à propos de ton ihm : si je devais travailler avec, elle me ferait péter les plombs !
    Réorganise tes VU's de telle sorte qu'ils présentent les infos de la même manière, sinon c'est complètement perturbant, le rouge un coup en bas à droite un coup en haut à droite un coup au milieu à droite, non non, ça n'est pas productif, àmha.

    La preuve ? En supposant que les valeurs des deux canaux "montent", à gauche l'aiguille va descendre quand celle de droite va monter
    Totalement illogique pour moi (qui ai passé une partie de ma jeunesse avec des consoles, des VU's, ce genre de choses).
    D'ailleurs les Vu's du bas de l'image montrent bien la logique de ces choses.

  14. #14
    Membre habitué

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    294
    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 : 294
    Points : 171
    Points
    171
    Billets dans le blog
    1
    Par défaut
    Réorganise tes VU's de telle sorte qu'ils présentent les infos de la même manière, sinon c'est complètement perturbant, le rouge un coup en bas à droite un coup en haut à droite un coup au milieu à droite, non non, ça n'est pas productif, àmha.
    Tu as évidemment raison. Pour le moment, je suis en train de mettre au point mes propres objets VuMeter, basés sur le tavail de Irnis Haliullin de 2002; mais avec une différence: mise à jour de l'affichage automatique sur changement du contenu d'une variable de type Integer, dans mon programme. Ainsi, j'ai 3 VuMeter, chacun attaché à une des 3 valeurs de son (gauche, global et droit). Et pour tester les affichages, jutilise les 3 représentations qui sont possibles pour cet objet. A terme, suivant l'utilisation, ce sera une des trois qui sera retenue pour l'utilisation finale (celle du milieu, certainement). Mais pour la phase des tests, je montre les trois pour être sûr que les trois fonctionnent.

    Par contre, si quelqu'un pouvait avoir une idée sur problème de base lié à la méthode GetChannelsPeakValues, ce serait vraiment un soulagement...

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

    Informations forums :
    Inscription : Juillet 2006
    Messages : 10 919
    Points : 15 356
    Points
    15 356
    Par défaut
    Bonsoir,

    J'ai trouvé une discussion sur un forum, qui montre comment utiliser la fonction qui te pose problème, mais je ne sais même pas en quel langage c'est écrit,
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    double waitPeriod; int channelCount
    IAudioMeterInformation meter=GetAudioMeter(waitPeriod channelCount)
     
    rep
    ,FLOAT peak; meter.GetPeakValue(peak)
    ,out peak
    ,
    ,;ARRAY(FLOAT) af.create(channelCount)
    ,;meter.GetChannelsPeakValues(af.len &af[0])
    ,;int i
    ,;for(i 0 af.len) out af[i]
    ,
    ,wait waitPeriod
    ,1 ;;debug

    Sinon, étudie ce code en Csharp.

    Et cette discussion avec du code en C++, peut-être la plus intéressante.


    Un qui m'a mis la haine, c'est celui qui dit qu'il a tout fait, bugs corrigés, sources fournies, mais lien mort...
    Je hais Internet pour ça


    Pas d'autres idées...

  16. #16
    Membre habitué

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    294
    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 : 294
    Points : 171
    Points
    171
    Billets dans le blog
    1
    Par défaut
    J'ai éudié les 3 codes. Je ne "parle" pas C, mais j'en sais suffisamment pour pouvoir lire un code et en comprendre la logique. J'ai isolé dans les 3 codes la partie qui récupère les valeurs PeakVolume des deux canaux, et je dois dire que je ne vois aucune différence avec ma propre implémentation. La méthode GetChannelsPeakValues de l'interface est appeléen dans tous ces cas, avec deux paramètres: le premier étant le nombre de canaux (2 dans ce cas), et le second est un pointeur vers un tableau de flottants simpre précision recevant les deux valeurs dans une fourchette entre 0.0 etg 1.0. Or, ça marche pour le premier canal (gauche) mais pour le second canal (droit), la valeur reste inchangée à zéro. Rien à faire.

    Effectivement, dans le 4ème exemple, celui où l'auteur a stipulé avoir résolu le problème, le lien vers le code est mort. Vraiment dommage, et difficile à comprendre lorsqu'in voit que la publication est relativement récente (2015).

    Je pense le le problème se situe au niveau de l'implémentation Delphi de l'interface, ou au niveau de la documentation qui est peut-être ambigue ou viciée quelque part. Est-ce que le tableau doit vraiment être déclaré comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ChannelVolumes: array[0..1] of Single;
    en Delphi pour recevoir les deux valeurs ? J'ai essayé avec Double, mais sans résultat. Je suis convaincu que le noed du problème se situe à cet endroit, au niveau du passage de paramètres entre la méthode de l'interface et mon code Delphi/

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

    Informations forums :
    Inscription : Juillet 2006
    Messages : 10 919
    Points : 15 356
    Points
    15 356
    Par défaut
    Citation Envoyé par KlausGunther Voir le message
    Je ne "parle" pas C, mais j'en sais suffisamment pour pouvoir lire un code et en comprendre la logique.
    Pareil.

    Citation Envoyé par KlausGunther Voir le message
    J'ai isolé dans les 3 codes la partie qui récupère les valeurs PeakVolume des deux canaux, et je dois dire que je ne vois aucune différence avec ma propre implémentation.
    Possible d'y jeter un coup d'œil, si tu les as conservées ?
    Parce que je ne peux guère aider au niveau des tests réels (je suis sous Linux, alors les API Windows, -- mais j'aime bien le son), mais des fois, en regardant du code avec un œil neuf...

    Citation Envoyé par KlausGunther Voir le message
    Or, ça marche pour le premier canal (gauche) mais pour le second canal (droit), la valeur reste inchangée à zéro. Rien à faire.
    Comment le vois-tu ?
    Tu as rajouté des TEdit pour afficher les contenus ? Tu loggues dans un TMemo ?
    Parce que si jamais (je vais dire une grosse bêtise) ton second VU était branché à l'envers et que l'aiguille essayait de descendre sous zéro ?

    Citation Envoyé par KlausGunther Voir le message
    Je pense le le problème se situe au niveau de l'implémentation Delphi de l'interface, ou au niveau de la documentation qui est peut-être ambigue ou viciée quelque part. Est-ce que le tableau doit vraiment être déclaré comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ChannelVolumes: array[0..1] of Single;
    en Delphi pour recevoir les deux valeurs ? J'ai essayé avec Double, mais sans résultat
    Faut bien examiner comment font les codes que j'ai trouvés.
    D'après MSDN ça devrait être correct : le float de MS semble correspondre au single de Delphi (mais à confirmer quand même).

    EDIT : regarde ce truc, ça doit être facilement transposable en Delphi, j'ai trouvé les noms des fonctions sympathiquement utiles (ou utilement sympathiques, comme tu le sens, )
    Salut, Y. !

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

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

    Informations forums :
    Inscription : Mars 2005
    Messages : 3 877
    Points : 11 373
    Points
    11 373
    Billets dans le blog
    6
    Par défaut
    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; 
        // msdn dit : HRESULT GetPeakValue( [out] float *pfPeak) et tu passes un Single
        function GetMeteringChannelCount(out pnChannelCount: UINT): HRESULT; stdcall;
        function GetChannelsPeakValues(u32ChannelCount: UINT; out afPeakValues: pSingle): HRESULT; stdcall; 
        // msdn dit HRESULT GetChannelsPeakValues( [in]  UINT32 u32ChannelCount, [out] float  *afPeakValues) et tu passes un pSingle (l'adresse d'un Single)
        function QueryHardwareSupport(out pdwHardwareSupportMask: UINT): HRESULT; stdcall;
      end;
    Je pense que le out sert à passer l'adresse de la variable, que l'API reçoit comme un pointeur : essaye de déclarer out afPeakValues: Single et de passer ChannelVolumes[0] si tableau zero-based ; ChannelVolumes[1] sinon.

  19. #19
    Membre habitué

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    294
    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 : 294
    Points : 171
    Points
    171
    Billets dans le blog
    1
    Par défaut
    Merci à vous deux ! Je vais creuser cela encore, avec ces informations.

    Voici comment je fais actuellement:
    Déclaration des variables!
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
      ChannelVolumes: array[0..1] 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
    Appel de la méthode:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
      ChannelVolumes[0] := 0;                   // assurer que le tableau est bien lis à zéro
      ChannelVolumes[1] := 0;
      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
     
      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...
    Et voici la fonction complète dont sont extaits ces codes:
    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[0..1] 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[0] := 0;                   // assurer que le tableau est bien lis à zéro
      ChannelVolumes[1] := 0;
      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 !
      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[0] * MaxOutput);             // valeur canal droit <======== valeur correcte !
      vR := Round(ChannelVolumes[1] * 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;
    J'avais mis des arrêts et affichages du contenu des variables partout, et particulièrement avant et après l'appel de la méthode. C'est pourquoi je peux affirmer que ChannelVolumes[0] reçoit bien la bonne valeur, et ChannelVolumes[1] reste déspérément à zéro. Et je ne me fie évidemment pas à l'affichage de mon VuMeter que je veux tester, justement, mais au contenu des variables que je surveille par des méthodes traditionnelles.

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

    Informations forums :
    Inscription : Juillet 2006
    Messages : 10 919
    Points : 15 356
    Points
    15 356
    Par défaut
    Salut, Y.
    Citation Envoyé par tourlourou Voir le message
    Je pense que le out sert à passer l'adresse de la variable, que l'API reçoit comme un pointeur : essaye de déclarer out afPeakValues: Single et de passer ChannelVolumes[0] si tableau zero-based ; ChannelVolumes[1] sinon.
    et moi je n'en suis pas si sûr ! Il me semble que chez MSDN ces [in] et [out] servent à indiquer le sens du mouvement des données, et ce [out] n'a rien à voir, àmha, avec le out et son synonyme var de Pascal.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    HRESULT GetChannelsPeakValues(
      [in]  UINT32 u32ChannelCount,
      [out] float  *afPeakValues
    );
    [in] = vers la méthode qui attend un uint32
    [out] = un float exporté par la méthode.

    Mais il est vrai que ces "*" ça met une joyeuse pagaille en termes de lisibilité et de compréhension du code : font chichi avec leurs pointeurs partout, on se croirait revenu au temps du langage machine, dur dur...

+ 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