Ircam - Centre Georges-Pompidou Équipe Analyse/Synthèse


Fonctionnement du spectrogramme de Xspect


  • Introduction
  • Structures de donnees de XSpect
  • Creation du spectrogramme
  • Affichage et manipulations sur le spectrogramme

    Introduction

    Le spectrogramme est une representation temps/frequence/amplitude d'un signal, l'amplitude etant representee par un degrade de couleurs.
    SuperVP a ete retenu pour la partie d'analyse, celle ci etant plus fastidieuse en passant par udi, comme les autres analyses. SuperVP est utilise pour generer un fichier qui contient une suite d'amplitudes sans entetes. En ce qui concerne l'affichage proprement dit, l'utilisation d'openGL semblait ideale, la tentative est decrite ici . Puisque finalement concu en X, il a fallu adapter XSpect au besoin de couleur plus important, et creer une structure pour concerver les informations relatives au degrade.

    Structures de donnees de XSpect

    Parametres de SuperVP

    Comme nous l'avons signale, le fichier que genere SuperVP ne contient qu'une suite de donnees brutes representant les amplitudes calculees. Il est necessaire, afin de parvenir a relire le fichier, de sauvegarder au minimum la taille de la fft, la taille de la fenetre d'analyse, et la taille du pas d'analyse.
    C'est egalement dans cette structure que vont etre ajoutes tous les parametres
    typedef struct _SvpOptions /* (modif 30/07/98) */
    /* any change must be reported to copySvpOptions */
    {
      int                fftSize;                  /* fft size in the super vp command line */
      int                windowSize;               /* window size */
      int                step;                     /* step size */
      STRING             output;                   /* s vp output (ampl or ampl_db) */
    }
    SvpOptions;
    
    Cette structure est utilisee dans le contexte d'analyse qui se situe dans le buffer (contexte d'analyse courante) et de view (contexte d'analyse par defaut). La structure de contexte d'analys est la suivante:
    typedef struct _AnalysisContext
    {
      /* any change must be reported to copyAnalysisContext */
      ... 
      short              type;                     /* type of analysis: ANALYSIS_TYPE_### */
      short              spectrumType;             /* type of scale: SPECTRUM_TYPE_### */
      ...  
      SvpOptions*        svpOptions;                /* options in the super vp command line */   
    }
    AnalysisContext;
    
    

    La structure ColorRamp

    Puisque la taille de la palette est variable, il faut stocker les indices de la premiere et de la derniere couleur du degarde. Ces variables sont donc dependantes du seul nombre de couleurs total.
    D'autre part, il est utile, pour ne voir que les partiels dont l'amplitude est la plus forte, de pouvoir limiter l'affichage a une certaine plage d'amplitude. C'est le role des entiers bDisplayRampAmpl et eDisplayRampAmpl. Leur valeur en dB peut etre modifiee par l'intermediaire de la boite de contexte colorEditor.
    typedef struct _ColorRamp /* (modif 03/08/98) */
    {  
      int                ibramp;                   /* index of the begining of the color ramp */
      int                ieramp;                   /* index of the end of the color ramp */
      int                bDisplayRampAmpl;         /* index of the begining ampl of the displayed color ramp */
      int                eDisplayRampAmpl;         /* index of the end amplitude of the displayed color ramp */
    }
    ColorRamp;
    
    Cette strucure pourrait dans un premier temps etre definie pour chaque view, mais le peu de de couleurs disponibles, dans la seule profondeur d'ecran maximale toleree par XSpect (8 bits, c'est a dire 256 couleurs), en tenant compte de l'utilisation de l'overlay qui oblige a reserver quatre fois plus de couleurs que necessaire, il ne reste pas suffisement de couleurs pour allouer plusieurs degrades. On utilise donc une seule ColorRamp, declaree dans une variable globale, tout en concervant une possibilite d'evolution facile. On declare donc :
    extern ColorRamp colorRamp; 
    

    L'option dBLimitForDisplay

    En sortie du fichier SuperVP, on obtient des amplitudes en dB qu'il n'est pas forcement utile de representer a l'ecran, et qui peuvent se reveler genantes: si l'on dispose de n couleurs pour afficher le sonagramme, celles ci seront egalement utilisees pour toute la profondeur du signal. De ce fait, la zone utile possede une definition moins bonne que ce qu'offre SuperVP.
    C'est pourquoi il a fallu creer l'option dBLimitForDisplay. Elle peut varier de -100 dB a 0 dB et represente l'amplitude minimale du signal dont on tient compte. Les couleurs dont ont dispose sont donc reparties entre dBLimitForDisplay et 0, zone qui gagane en definition, la couleur entre le minimum et dBLimitForDisplay sont representees dans la couleur la plus basse (d'indice ibramp). On a donc cree l'option suivante qui peut etre donnee dans la ligne de commande de Xspect, ou changee dans le panneau d'options du menu context.
    typedef struct _Options
    {
      ...
      int                dBLimitForDisplay;          /* (modif 04/09/98)*/ 
      ...
    }
    *OptionsPtr, Options;
    

    Creation du spectrogramme

    Introduction

    La creation de nouvelles analyses similaires a celles deja existantes (analyse par udi, resultat bidimentionnel) ne pose pas de probleme. Ainsi, l'affichage de la phase n'a pas pris beaucoup de temps. Il n'en est pas de meme pour le spectrogramme dont l'analyse est faire par SuperVP, programme externe au lieu de la bibliotheque C udi et dont le resultat dispose d'une dimension supplementaire.
    De meme, si les fonctions servant a effectuer un pipe et a lire un fichier en memory mapping (c'est a dire en traitant le fichier comme une zone memoire) existaient bien, elles n'etaient pas du tout generiques, mais specifiques a l'utilisation qui en avait ete faite jusque la.

    Nous allons maintenant suivre la construction de l'analyse pas a pas, dans le but de voir precisement la facon dont les fonctions sont utilisees.

    Creation du buffer d'analyse du spectrogramme

    La premiere etape de l'analyse consiste a creer un buffer qui va pouvoir contenir le spectrogramme. Pour le spectrogramme, la Nature de buffer correspondant au spectrogramme existait. Il a par contre fallu creer un Type de Buffer, qui decrit la facon dont sont ordonnees les donnees. Les definitions sont les suivantes :
    #define BUFFER_NATURE_spectrogram  3        /* time x frequency x amplitude */
    #define BUFFER_TYPE_regularSampleTrames 6   /* (modif 29/07/98) */
    
    Le type du buffer, regularSampleTrames signifie que les donnees sont ordonnees par trames, une trame correpondant a un echantillon temporel, ces trames etant regulierement espacees. La creation du buffer est effectuee dans la fonction askCallUdi. Cette fonction initialise principalement deux parties de la structure du buffer:
  • Les elements qui le definissent independement de tout contenu. Ainsi, on pourra deduire du type BUFFER_NATURE_spectrogram que la dimension du buffer est DIM_2, que le type du buffer est BUFFER_TYPE_regularSampleTrames, et qu'il faut initialiser la structure correpondant au fichier associe
  • La strucure source du buffer, a partir de la view sur laquelle est effectuee l'analyse.

    La fonction utilisee est :

    void askCallUdi(View *viewParam,View *viewFromParam) (du fichier analyse.c)
    
    Il faut noter que cette meme fonction est appele lorsque une analyse est refaite (Redo Analysis du menu d'analyse). Dans ce cas, l'etape de creation et d'initialisation du buffer est sautee. Contrairement a ce qui se passait dans le passe, un buffer ne peut pas changer de nature lorsqu'une analyse est refaite: si on change le type d'analyse dans une view, un nouveau buffer est cree puis place dans cette vue par la suite, pour eviter des problemes d'incoherence.

    Preliminaires a l'analyse

    Si la portion du signal a utiliser est trop longue et que l'utilisateur a souhaite en etre averti (options->nbSamplesBeforeWarning non placee a zero), un message est affiche qui permet la confirmation de l'analyse. Ensuite, on appele une fonction qui va commencer l'analyse sur un segment de la source:
    void doUDI(View* uneView,Buffer* unBuffer)
    
    Contrairement a ce que suggere son nom, comme c'est le cas de la fonction precedente, doUDI est utilise a la fois pour les analyses udi et SuperVP, son nom etant du a des raisons historiques. Apres avoir teste
  • l'existence,
  • le type,
  • la coherence,
  • la taille de la selection sur laquelle va etre effectuee l'analyse, on lance la fontion d'analyse attachee a la nature de l'analuse. Dans notre cas, on utilise la fonction callSvpSpectrogram qui recoit la view sur laquelle est effectuee l'analyse et le buffer destine a recevoir l'analyse.

    Une fois le buffer construit comme nous allons dans le voir, Le buffer est lie a la view (s'il vient d'etre cree), la structure des donnes est mise a jour (minimas et maximas de chacun des axes, dans update.c) puis affiche.
    De plus, si des synchronisations sont posees sur cette view, c'est a la fin de cette fonction qu'elles vont etre gerees.

    Construction du fichier SuperVP

    void  callSvpSpectrogram(View* viewFrom,Buffer* bufferTo) 
    
    Le deroulement de cette fonction du fichier svpAnalysis.c (qui, par ailleurs, est abondement commentee dans le programme) est le suivant :

  • Construction du nom de l'analyse, par un appel a la fonction getAnalysisFilename du meme fichier. Le format retenu pour cela est devait etre unique a tous les instants de l'execution. La fonction et le format sont les suivants:
    getAnalysisFilename(nom_de_fichier,buffer) et 
    nom = $SFDIR/tmp.xspect.[pid].[ref du buffer]
    
  • construction de la ligne de commande SuperVP, en utilisant la structure svpOptions du contexte d'analyse, par la fonction suivante:
    buildSvpCommandLine(chaine_de_commande,view_from,Buffer_d_analyse,nom_de_fichier) 
    
  • Pipe de la selection dans SuperVP et execution de la commande precedente par la fonction suivante:
    executeOutputCommand(viewFrom,command);
    
  • Si l'execution de SuperVP est reussie, on rempli de nouveaux champs du buffer, et on effectue le memory mappind du fichier obtenu par la fonction:
    mmapBufferData(buffer);
    
    Cette fonction est une adaptation de la fonction de mapping qui etait utilisee precedement, de maniere a la rendre plus generique. De fait, c'est aussi cette fonction qui est utilisee lors du chargement de fichiers dansl e mode Visit.

    Affichage et manipulations sur le spectrogramme

    Affichage du spectrogramme

    L'affichage du spectrogramme est effectue dans le fichier plotSegement.c, ou l'on trouve egalement la fonction de trace des signaux et des analyses issues de udi, dans la fonction:
    void MakeXSpectrogram(View* uneView,DisplayContext* unDispCt)
    Cette fonction, ainsi que les fonctions qu'elle utilise sont commentees precisement au sein du programme . Nous n'allons donc voire que son principe de base.

    L'affichage est effectue en X. On parcoure le pixmap (de type XImage) point par point a travers une boucle verticale (frequence) et une boucle horizontale (temps). Pour chaque pixel, on doit determiner l'amplitude, puis la couleur correpondante. Pour cela, il faut pouvoir determiner la valeur qui correpond a un couple (x,y) du pixmap.
    Pour ces conversions, il est necessaire d'extraire de la structure SvpOptions de l'analyse la distance en pixels ou en secondes entre deux trames, la distance en pixels ou en hertz entre deux donnees d'une meme trame, etc. Ces donnees peuvent etre obtenues a partir des fonctions du fichier svpAnalysis.c que sont

    /*
     * returns the size of the Frame for a BUFFER_TYPE_regularSampleTrames
     * returns 1 otherwise
     */ 
    int getTrameSize(Buffer* unBuffer)
    {}
    
    /*
     * returns the time between two steps of an super vp analysis
     */ 
    float getTrameTimeStep(Buffer* unBuffer)
    {}
    
    /*
     * returns the frequency interval between two values
     * of a same trame (modif 16/08/98)
     */ 
    float getTrameFreqStep(Buffer* unBuffer)
    {}
    
    /*
     * returns the number of pixels between two trames
     */
    float getNbPixelsBetweenTrames(View* uneView)
    {}
    
    /*
     * returns the number of pixels between two datas of a trame
     */
    float getNbPixelsBetweenDatas(View* uneView)     
    {}
    
    Ensuite, il reste a faire correpondre une amplitude absolue de la donnee correpondant au buffer a la couleur correpondant a une intensite en dB. C'est ce que fait la fonction:
    int getColorIndex(amplitude, unData)
    Cette fonction tien compte de la limite d'affichage, et des maximum et minimum de la donnee.

    Manipulation de la palette de couleurs

    Pour permettre les manipulations de couleurs que nous avons decrites plus haut, il a fallu creer une boite de contexte dans laquelle sont stockes affichees et peuvent etre modifiees les valeurs:
    bDisplayRampAmpl et 
    eDisplayRampAmpl
    
    Cette boite est une boite de contexte standard. Elle est definie dans le fichier dialogColor.c. Une variable globale est egalement definie, par souci de coherence avec les autres boites de contexte, et se nomme colorEditors[]. Lors de l'appuis sur le bouton apply, ou de tout deplacement si le mode auto-apply est activite, la nouvelle palette de couleur est calculee dans une fonction de color.c:
    void StoreColorRampInColormap(aColormap,beginIndex,endIndex,beginRampIndex,endRampIndex);
    
    Cette fonction repartit lineairement, du blanc au noir, des niveaux de gris correpondants aux amplitudes situees entre bDisplayRampAmpl et eDisplayRampAmpl. En dessus et en deca de ces seuils les couleurs extremes de la palettes sont utilisees. Pour convertir les valeurs en dB obtenues dans la structure ColorRamp en indexes correpondants, on fait appel a la fonction de color.c getColorIndexFromAmplInDb. La fonction effetcuant l'operation inverse existe egalement et se nomme getAmplIndBFromColorIndex.
    Au sein de la fonction StoreColorRampInColormap , on fait appel a la fonction
    void storeRVBColor(iCoul,red,green,blue)
    
    Celle ci recupere un pointeur sur la cellule du tableau de cellules de couleur exactDefs correpondante a l'indice iCoul chaque intensite RVB par la primitive XQueryColor. Puisque elle fait a son tour appel a la primitive X XStoreColor, qui ne peut etre utilisee que sur des palettes accessibles en lecture/ecriture (ce qui est le cas du fait de l'allocation de ColorCells en mode read/write, lorsque le materiel le permet).


    Derniere modification : 15/09/98 - Vivien Guillet