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

C# Discussion :

Affichage signal audio en temps réel


Sujet :

C#

  1. #1
    Membre du Club
    Inscrit en
    Octobre 2005
    Messages
    63
    Détails du profil
    Informations forums :
    Inscription : Octobre 2005
    Messages : 63
    Points : 68
    Points
    68
    Par défaut Affichage signal audio en temps réel
    Salut a tous,
    J'ai un soucis d'optimisation au niveau de l'affichage temps reel d'un signal audio.

    Dans mon application, je veux afficher 10 secondes d'un signal audio acquis à 16KHz. Ca fait donc 160 000 points à afficher. Le signal s'affiche par un defilement de gauche à droite (buffeur circulaire). Pour le moment j'ai deux methodes pour afficher mes courbes mais aucune ne me donne satisfaction.

    La première est la suivante:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    for (i = 0; i < this.Width - 1; i++)
      {
        g.DrawLine(P1, i, this.Height - 5 - (int)Math.Round((waveform1[(int)Math.Round(i / xS)] - maxmin1[1]) * yS), i + 1, this.Height - 5 - (int)Math.Round((waveform1[(int)Math.Round((i + 1) / xS)] - maxmin1[1]) * yS));
      }
    En gros, à chaque refresh, je dessine 160 000 / xS points sur toute la largeur this.Width de mon image bitmap dans laquelle j'affiche la courbe du signal. Le problème de cette methode c'est qu'en plus du scintillement du au refresh, j'ai un scintillement au niveau de la courbe elle meme. La raison étant qu'à chaque fois que je redessine la courbe en entier, le downsampling d'un facteur xS et le buffeur circulaire du signal font qu'une valeur acquise à t0 n'a pas la meme faleur entre l'affichage à t et l'affichage à t + 1.

    Et la seconde est celle-ci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    for (i = 0; i < timeWindow - 1; i++)
      {
        g.DrawLine(P1, (int)Math.Round(i * xS), this.Height - 5 - (int)Math.Round((waveform1[i] - maxmin1[1]) * yS), (int)Math.Round((i + 1) * xS), this.Height - 5 - (int)Math.Round((waveform1[i + 1] - maxmin1[1]) * yS));
      }
    Cette fois, pas de downsampling, je dessine tous les points (timeWindow = 160 000), l'affichage est beaucoup plus stable car le signal acquis à t0 a la meme valeur entre 2 refresh consecutifs. Par contre, le refresh rate chute enormement à cause du nombre de points à dessiner.

    Quelqu'un aurait-il une solution pour un affichage stable et rapide?

    Merci de m'aider

  2. #2
    Membre confirmé
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2009
    Messages
    317
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Finance

    Informations forums :
    Inscription : Février 2009
    Messages : 317
    Points : 560
    Points
    560
    Par défaut
    Bonjour,

    Dans un premier temps, as tu besoin d'afficher l'entièreté de tes 160.000 points pour avoir un affichage convenable de ta courbe ?

  3. #3
    Expert confirmé

    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Septembre 2006
    Messages
    3 580
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Chef de projet NTIC
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Septembre 2006
    Messages : 3 580
    Points : 5 195
    Points
    5 195
    Par défaut
    salut

    une solution simple à mettre en oeuvre:

    Tu dessines dans une bitmap... et au fur et à mesure que tu dois scroller,
    tu décales la bitmap et tu mets une deuxième bitmap que tu crées dynamiquement...

    En utilisant une telle technique, le premier "Draw" va prendre un peu de temps car tu vas créer et remplir ta bitmap, mais après, tu n'auras plus qu'à,
    soit rajouter des points à ta bitmap et la décaler vers la gauche, soit créer une deuxième bitmap collée à droite de la première et dans laquelle tu ajouteras les points...

    Si tu dois redessiner 160000 points à chaque fois, c sur que ca va un peu ramer...

    Une autre astuce est d'appeler DrawLines en passant le tableau des points calculés comme il faut car cela sera plus rapide que xx appels à DrawLine.

    De plus, calculer à chaque fois Math.Round(x) n'est pas pertinent.
    Une fois qu'un point a été calculé en Y, le x changera de -1 à chaque décalage vers la gauche, mais pas le Y.. donc, le recalculer à chaque fois est source de perte de temps importante...
    The Monz, Toulouse
    Expertise dans la logistique et le développement pour
    plateforme .Net (Windows, Windows CE, Android)

  4. #4
    Inactif  
    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Janvier 2007
    Messages
    6 604
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France

    Informations professionnelles :
    Activité : Chef de projet NTIC

    Informations forums :
    Inscription : Janvier 2007
    Messages : 6 604
    Points : 13 314
    Points
    13 314
    Par défaut
    Citation Envoyé par Aeronia Voir le message
    Dans un premier temps, as tu besoin d'afficher l'entièreté de tes 160.000 points pour avoir un affichage convenable de ta courbe ?
    C'est un peu la question que je me posais.

    Je ne réponds pas aux questions techniques par MP ! Le forum est là pour ça...


    Une réponse vous a aidé ? utiliser le bouton

    "L’ennui dans ce monde, c’est que les idiots sont sûrs d’eux et les gens sensés pleins de doutes". B. Russel

  5. #5
    Membre du Club
    Inscrit en
    Octobre 2005
    Messages
    63
    Détails du profil
    Informations forums :
    Inscription : Octobre 2005
    Messages : 63
    Points : 68
    Points
    68
    Par défaut
    Citation Envoyé par Aeronia Voir le message
    Bonjour,

    Dans un premier temps, as tu besoin d'afficher l'entièreté de tes 160.000 points pour avoir un affichage convenable de ta courbe ?
    Il semblerait que ce soit le cas, oui.

  6. #6
    Membre du Club
    Inscrit en
    Octobre 2005
    Messages
    63
    Détails du profil
    Informations forums :
    Inscription : Octobre 2005
    Messages : 63
    Points : 68
    Points
    68
    Par défaut
    Citation Envoyé par theMonz31 Voir le message
    salut

    une solution simple à mettre en oeuvre:

    Tu dessines dans une bitmap... et au fur et à mesure que tu dois scroller,
    tu décales la bitmap et tu mets une deuxième bitmap que tu crées dynamiquement...

    En utilisant une telle technique, le premier "Draw" va prendre un peu de temps car tu vas créer et remplir ta bitmap, mais après, tu n'auras plus qu'à,
    soit rajouter des points à ta bitmap et la décaler vers la gauche, soit créer une deuxième bitmap collée à droite de la première et dans laquelle tu ajouteras les points...

    Si tu dois redessiner 160000 points à chaque fois, c sur que ca va un peu ramer...

    Une autre astuce est d'appeler DrawLines en passant le tableau des points calculés comme il faut car cela sera plus rapide que xx appels à DrawLine.

    De plus, calculer à chaque fois Math.Round(x) n'est pas pertinent.
    Une fois qu'un point a été calculé en Y, le x changera de -1 à chaque décalage vers la gauche, mais pas le Y.. donc, le recalculer à chaque fois est source de perte de temps importante...
    Merci, je vais essayer de mettre tout ca en place.

  7. #7
    Membre du Club
    Inscrit en
    Octobre 2005
    Messages
    63
    Détails du profil
    Informations forums :
    Inscription : Octobre 2005
    Messages : 63
    Points : 68
    Points
    68
    Par défaut
    Bon, j'ai essayé plusieurs choses avec plus ou moins de succès mais il y a un problème que je n'arrive pas à régler c'est le flickering du panel au refresh.

    Mon application est de type Windows Forms et j'utilise un custom panel pour afficher la courbe. J'ai copié le bout de code suivant dans le constructeur de mon panel pour essayer d'activer le double buffer mais ca n'a fait qu'empirer les choses (scintillement encore plus fort).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    this.SetStyle(ControlStyles.DoubleBuffer |
                          ControlStyles.UserPaint |
                          ControlStyles.AllPaintingInWmPaint, true);
    this.UpdateStyles();
    Est ce que j'aurai plus de chance en passant en WPF?

  8. #8
    Expert confirmé

    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Septembre 2006
    Messages
    3 580
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Chef de projet NTIC
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Septembre 2006
    Messages : 3 580
    Points : 5 195
    Points
    5 195
    Par défaut
    question bete:

    Le Paint de ton panel, ou est-il fait ?

    Dans la fonction OnPaint ou bien dans un "timer" maison ?

    Peut-etre que si tu "publies" ici le code de ton controle on pourra travailler dessus
    The Monz, Toulouse
    Expertise dans la logistique et le développement pour
    plateforme .Net (Windows, Windows CE, Android)

  9. #9
    Membre du Club
    Inscrit en
    Octobre 2005
    Messages
    63
    Détails du profil
    Informations forums :
    Inscription : Octobre 2005
    Messages : 63
    Points : 68
    Points
    68
    Par défaut
    C'est bon, après avoir regardé sur le net et réfléchi un peu (j'aurais peut être du attendre un peu avant de poster ici) j'ai trouvé une solution efficace.

    D'abord, le premier truc, c'est que les panels des winforms ne peuvent apparemment pas beneficier du double buffering, seuls les forms le peuvent. J'ai donc fait hériter ma classe "Graph" chargée d'afficher la courbe du signal de Form au lieu de la faire heriter de Panel. J'ai donc pu activer le double buffering et l'affichage de la courbe s'effectuait donc sans le scintillement du au single buffering. Code pour activer le double buffering:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    this.SetStyle(
                    ControlStyles.AllPaintingInWmPaint |
                    ControlStyles.UserPaint |
                    ControlStyles.DoubleBuffer, true);
    A mettre dans le constructeur apres le InitializeComponent();


    La deuxieme modification a été de trouver quels points afficher. J'ai tenté plusieurs choses, dont l'utilisation de DrawLines au lieu de DrawLine mais sans succès. 160 000 points à dessiner ca demandait trop de ressources. J'ai donc réeaxminé ma facon de downsampler pour faire en sorte d'avoir le meme affichage que sans downsampling.

    Pour rappel, ma premiere solution etait, pour une fenetre de 1000 pixels de large, d'afficher les pixels i qui verifiaient i%1000 == 0 parmi les 160 000. Le probleme c'est que sur un buffeur circulaire, au fur et à mesure qu'on y ajoute des points, si le nombre de points n'est pas un multiple de la fenetre de downsampling (ici 160 000 / 1000 = 160) on crée un decalage et on utilise pas les memes points entre deux refresh pour l'affichage de la courbe. Resultat, ca scintille.

    En essayant de resoudre ce probleme, je me suis rendu compte d'un autre probleme. Il faut aussi choisir une fenetre d'affichage ayant comme largeur un multiple de la taille du tableau contenant le signal. Donc une largeur de 1000px pour 160 000 points c'est bon, mais 1001px pour 160 000 points et on a un scintillement qui apparait. Logique quand on y pense, mais j'y avais pas pensé.

    J'espère que j'ai ete clair jusque là. Maintenant que les problèmes ont été identifiés c'est assez facile de les resoudre. Il suffit à chaque refresh "d'oublier" de dessiner les derniers points acquis du signal dont le nombre est egal à (nombreTotalDePointsAcquis % tailleDeLaFenetreDeDownsampling). De cette facon on se realigne avec le découpage en fenêtre de downsampling.

    Pour choisir quel seul et unique point afficher parmi ceux de la fenetre de downsampling, j'ai choisi d'afficher un trait d'amplitude = maximum - minimum de cette fenêtre car c'est ce qui serait apparu au final si j'avais affiché tous les points.

    Le bout du code qui est dans le OnPaint, ya surement des optimisations à effectuer mais le resultat est dors et deja celui que je desirai et ca pourra peut-être servir à quelqu'un:

    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
    pe.Graphics.FillRectangle(new SolidBrush(this.BackColor), this.ClientRectangle);
                    using (Pen pen = new Pen(Color.Red))
                    {                
                        // W is the number of pixels used to display signal x
                        int W = this.Width;
     
                        // Set W length so that the result of this.x.Length / W is an integer (anti-flickering)
                        while (this.x.Length % W != 0)
                            W--;
     
                        // Get the downsampling increment
                        int binsPerPixel = (this.x.Length / W);
                        if (binsPerPixel == 0)
                            binsPerPixel = 1;
     
                        // Number of new samples may not be a modulo of the downsample increment
                        // so new samples to draw may not be aligned relative to binsPerPixel and the previously drawn samples.
                        // Drawing will thus only start at currentBin to have binsPerPixel windows aligned with the ones from
                        // previous drawing
                        int currentBin = this.samplesRead % binsPerPixel;
     
                        // Variables for drawing the signal waveforms
                        double currentPointY1 = 0;
                        double currentPointY2 = 0;
                        double amplitude = 0;
                        double maxValue = -99999;
                        double minValue = 99999;
     
                        // Get maximum and minimum of the signal and calculate amplitude
                        GetSignalLimits();
                        amplitude = this.maximumSignal - this.minimumSignal;
     
                        // Draw pixel by pixel
                        for (int pixel = 0; pixel < W; pixel++)
                        {
                            maxValue = -99999;
                            minValue = 99999;
     
                            // Because of the downsampling the only values of interest 
                            // are the maximum and the minimum of the signal
                            // in a binsPerPixel wide window as there would be the one that would show
                            // if all samples were drawn. (anti-flickering)
                            for (int bin = 0; bin < binsPerPixel; bin++)
                            {
                                if(currentBin + bin < x.Length)
                                {
                                    // Get maximum and minimum for the window
                                    if (x[currentBin + bin] > maxValue)
                                        maxValue = x[currentBin + bin];
                                    if (x[currentBin + bin] < minValue)
                                        minValue = x[currentBin + bin];
                                }
                                else
                                {
                                    // Get maximum and minimum for the window
                                    if (x[x.Length - 1] > maxValue)
                                        maxValue = x[x.Length - 1];
                                    if (x[x.Length - 1] < minValue)
                                        minValue = x[x.Length - 1];
                                }
                            }
                            currentBin += binsPerPixel;
     
                            if (amplitude > 0)
                            {
                                // Get coordinates of the points to plot
                                currentPointY1 = this.Height - this.Height * (minValue - this.minimumSignal) / amplitude;
                                currentPointY2 = this.Height - this.Height * (maxValue - this.minimumSignal) / amplitude;
     
                                // Draw
                                pe.Graphics.DrawLine(pen, pixel, (int)(currentPointY1), pixel, (int)(currentPointY2));
                            }
                        }
                    }

    Moralité, vaut mieux utiliser un buffer classique et non circulaire, ca evite tous ces problemes ou presque

    PS: désolé pour les accents, j'ai un clavier UK. Pour les fautes, j'ai pas d'excuse

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. MFC - affichage de données en temps réel
    Par limprid dans le forum MFC
    Réponses: 2
    Dernier message: 13/06/2012, 20h04
  2. Envoi flux audio en temps réel avec RTP
    Par cool147 dans le forum Multimédia
    Réponses: 3
    Dernier message: 28/04/2009, 13h36
  3. Affichage d'un compteur temps réel
    Par Karcynotron dans le forum MFC
    Réponses: 1
    Dernier message: 27/05/2008, 21h47
  4. Affichage de traces en temps réel -- CEdit
    Par vonemya dans le forum MFC
    Réponses: 4
    Dernier message: 23/08/2007, 15h53

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