Manuel d'utilisation de la librairie SDIF

# LA LIBRAIRIE SDIF

# Manuel d'utilisation des fonctions en C

Analyse/Synthèse

Dominique Virolle

- Au départ...
  - Compilation
  - Initialisation
  - Ouvrir et Fermer un fichier SDIF
  - Méthodologie des noms
- Opérations sur SdifFileT*
  - Où trouver des exemples?
  - Opérations indépendantes du mode d'ouverture
    - Opérations sur la base de données de SdifFileT*
      - Opérations sur NameValues
      - Opérations sur MatrixTypesTable
      - Opérations sur FrameTypesTable
      - Opérations sur StreamIDsTable
    - Opérations sur les éléments temporaires de SdifFileT*
  - Opérations sur le fichier binaire
    - Ecriture
    - Lecture
  - Conversions
- Ecriture d'un fichier pseudo-SDIF texte
  - Exemple de fichier pseudo-SDIF

# Au départ...

- Compilation
- Initialisation
- Ouvrir et Fermer un fichier SDIF
- Méthodologie des noms

## Compilation

Il est nécessaire pour utiliser la librairie SDIF de connaître son emplacement (Actuellement dans: ~virolle/src/SDIF/libsdif/(system)). Les sources sont sous CVS ($CVSROOT), le projet est SDIF ($CVSROOT/SDIF). Aussi, il est nécessaire de connaître l'emplacement de `sdif.h` (Actuellement dans: ~virolle/src/SDIF/sdif). De plus il faudra que l'executable est accès au fichier `SdifTypes.STYP` qui est un fichier texte contenant la définition des types prédéfinis de SDIF (Actuellement dans: ~virolle/src/SDIF/libsdif). Il se peut que l'administrateur système décide de n'avoir qu'une seule copie de ce fichier. Ainsi, il faut connaître le path associé.

Lorsque tous ces accès fichiers sont résolus, inclure les paths dans le(s) makefile(s) pour `libsdif.a` et `sdif.h`. Ajouter l'option `-lsdif` au linkage du projet (cf. makefile du test de la librairie Chant avec SDIF dans $CVSROOT/libchant/test/tsdif). Dans tous les fichiers sources utilisant Sdif ajouter un `#include Sdif.h`.

Important: En cas de recompilation de la libraire, lire la première partie de la documentation du programmeur concernant les dépendances machines, systèmes et compilateurs (32 ou 64 bits, le type `fpos_t`, Little ou Big endian).

## Initialisation

Avant d'utiliser les fonctions SDIF, il est nécessaire d'initialiser la librairie, ce qui consiste à charger les types prédéfinis du fichier `SdifTypes.STYP`. La fonction à utiliser est `SdifGenInit(char*)` où l'argument est le nom du fichier des types prédéfinis avec le path. Il suffit d'une seule initialisation par execution du programme pour manipuler plusieurs fichier SDIF. Les types prédéfinis sont stockés dans une variable globale SdifFileT* gSdifPredefinedTypes.

A la fin de tous les traitements de fichiers SDIF, il est préférable d'annuler l'initialisation de SDIF avec la procédure `SdifGenKill(void)`. Ainsi toute la mémoire allouée par l'initialisation est rendue au système.

Dans le cas où le fichier des types prédéfinis est introuvable, la librairie utilise les types qu'elle contient en hard-codé. Cependant, tous les types n'y sont peut-être pas et un message d'erreur est écrit sur `stderr`.

## Ouvrir et Fermer un fichier SDIF

La librairie est conçu pour manipuler les fichiers SDIF avec des fonctions d'assez haut niveau. Il n'est normalement pas nécessaire d'utiliser les fonctions de stdio ou de iostream. Ainsi, la plupart des fonctions s'applique à un pointeur sur `SdifFileT` qui est une structure contenant toutes les informations d'un fichier et plus particulièrement le flux `FILE *Stream`. L'ouverture et la fermeture d'un fichier SDIF ressemble fortement aux méthodes classiques avec `FILE*`. On utilise `SdifFileT* SdifOpenFile(char*, int)` et `SdifCloseFile(SdifFileT*)`. Exemple:

    #include "sdif.h"

    void main(void)
    {
      SdifFileT *MySdifFileToWrite;
      SdifFileT *MySdifFileToRead;

      SdifGenInit("SdifTypes.STYP");

      MySdifFileToWrite = SdifOpenFile("NewFile.sdif",      eWriteFile);
      MySdifFileToRead  = SdifOpenFile("ExistingFile.sdif", eReadFile);
      /* ... */
      SdifCloseFile(MySdifFileToWrite);
      SdifCloseFile(MySdifFileToRead);

      SdifGenKill();
    }

On peut remarquer dans cet exemple les deux modes d'ouverture d'un fichier SDIF:

- eWriteFile pour écrire un nouveau fichier SDIF,
- eReadFile pour lire un fichier SDIF existant.

Il existe un troisième mode mais qui est réservé à l'administrateur de la librairie. Si le fichier en lecture est introuvable, alors on a un message sur stderr et une sortie violante `exit(1)`

## Méthodologie des noms

Le code de la librairie SDIF est construit avec un formalisme au niveau des noms des structures, des variables et des fonctions. Il est souhaitable pour les futurs maintenances de SDIF que les noms des fonctions utilisateurs ne commencent pas par 'Sdif'. En particulier, ne pas nommer une fonction SdifFReadMatrix ou SdifFReadFrame...

# Opérations sur SdifFileT*

- Où trouver des exemples?
- Opérations indépendantes du mode d'ouverture
  - Opérations sur la base de données de SdifFileT*
    - Opérations sur NameValues
    - Opérations sur MatrixTypesTable
    - Opérations sur FrameTypesTable
    - Opérations sur StreamIDsTable
  - Opérations sur les éléments temporaires de SdifFileT*
- Opérations sur le fichier binaire
  - Ecriture
  - Lecture
- Conversions

## Où trouver des exemples?

(12/1997). Il existe plusieurs utilisations de la librairie:

- Dans la librairie Chant se trouve un lecteur-synthètiseur (développement de ce module: Dominique Virolle (Ircam)),
- Dans le programme Diphone (Macintosh) se trouve un importeur (lecteur) et un exporteur (écrivain) avec les mêmes connaissances que Chant (développement: Adrien Lefèvre (Ircam)),
- Dans la librairie SDIF elle-même, se trouve un lecteur et un écrivain pour les conversions avec des fichiers pseudo-SDIF texte. Mais, il n'y a pas d'utilisations des données.

## Opérations indépendantes du mode d'ouverture

### Opérations sur la base de données de SdifFileT*

Dans cette partie sur les opérations modifiant la base de données associée à un fichier, on considère que SdifF est un pointeur sur le type SdifFileT alloué, soit:

    SdifFileT* SdifF;
    /* ... */
    SdifF = SdifOpenFile("Name", OpenOption);
    /* où OpenOption est, soit eReadfile, soit eOpenFile
    */

#### Opérations sur NameValues

SdifNameValuesLT* SdifNameValuesLNewHT (SdifNameValuesLT *NameValuesL)
Cette fonction permet d'ajouter une nouvelle NVT dans la liste de tables passée par argument: `SdifNameValuesLNewHT(SdifF->NamefValues);`
Attention, à l'ouverture de SdifF, il n'y a aucune table dans SdifF->NamefValues. Il faudra donc au moins en ajouter une pour pouvoir y mettre des NameValue.

SdifNameValueT* SdifNameValuesLPut (SdifNameValuesLT *NameValuesL, char *Name, char *Value)
Cette fonction permet d'ajouter une NameValue à table courante qui est la dernière table créée ou celle définie en tant que table courante. Name et Value doivent être des chaines caractères ASCII sans espacements.

SdifHashTableT* SdifNameValuesLSetCurrHT (SdifNameValuesLT *NameValuesL, SdifUInt4 NumCurrHT)
Cette fonction permet de définir la nième NVT de la liste des tables comme NVT courante.

SdifNameValueT* SdifNameValuesLGet (SdifNameValuesLT *NameValuesL, char *Name)
Cette fonction permet de récupérer une Name-Value de la liste des NVTs en passant le Name en argument. Dans le cas ou Name est référencé dans plusieurs NVT, alors c'est la première NVT le contenant qui sera prise en compte. Le pointeur retourné est de type SdifNameValueT qui contient deux champs: Name et Value.

SdifNameValueT* SdifNameValuesLGetCurrHT (SdifNameValuesLT *NameValuesL, char *Name)
Cette fonction réalise aussi une requête en fonction de Name mais uniquement dans la NVT courante.

Exemples:

    {
      SdifNameValueT *NV;

      /* adition de NVTs et de Name-Values dans chaque NVT */

      /* first table */
      SdifNameValuesLNewHT(SdifF->NameValues);
      SdifNameValuesLPut(SdifF->NameValues, "NameTable", "General");
      /* second table */
      SdifNameValuesLNewHT(SdifF->NameValues);
      SdifNameValuesLPut(SdifF->NameValues, "NameTable", "analyse");
      SdifNameValuesLPut(SdifF->NameValues, "WindowSize", "512");
      SdifNameValuesLPut(SdifF->NameValues, "SamplingRate", "32000.0");
      /* third table */
      SdifNameValuesLNewHT(SdifF->NameValues);
      SdifNameValuesLPut(SdifF->NameValues, "NameTable", "synthesis");
      SdifNameValuesLPut(SdifF->NameValues, "NumberOfChannels", "2");
      SdifNameValuesLPut(SdifF->NameValues, "EndTime", "1.0");
      SdifNameValuesLPut(SdifF->NameValues, "SamplingRate", "44100.0");

      /* Requêtes */

      NV = SdifNameValuesLGet(SdifF->NameValues, "NameTable");
      /* alors NV est ("NameTable", "General") */
      NV = SdifNameValuesLGetFromCurrHT(SdifF->NameValues, "NameTable");
      /* alors NV est ("NameTable", "synthesis") */
      SdifNameValuesLSetCurrHT(SdifF->NameValues, 2);
      NV = SdifNameValuesLGetFromCurrHT(SdifF->NameValues, "NameTable");
      /* alors NV est ("NameTable", "analyse") */
      NV = SdifNameValuesLGet(SdifF->NameValues, "EndTime");
      /* alors NV est ("EndTime", "1.0") */

    }

#### Opérations sur MatrixTypesTable

SdifMatrixTypeT* SdifCreateMatrixType (SdifSignature Signature, SdifMatrixTypeT *PredefinedMatrixType)
premet de créer un objet 'type de matrice'. Le premier argument est la signature de ce type. Le second est l'objet 'type de matrice' prédéfini dans SDIF.

Important: Tous les types de matrices ou de frames utilisés dans une instance de SdifFileT doivent être ajoutés aux tables de cette instance, de façon a créer le lien avec les types prédéfinis. L'hors de la lecture des entêtes avec les fonctions SdifFReadMatrixHeader et SdifFReadFrameHeader, cette mise à jour se fait automatiquement à l'aide des fonctions SdifTestMatrixType et SdifTestFrameType.

SdifMatrixTypeT* SdifTestMatrixType (SdifFileT* SdifF, SdifSignature Signature)
Cette fonction vérifie si le type de matrice est répertorié dans SdifF.
S'il ne l'est pas, alors elle vérifie si c'est un type prédéfinis. S'il est prédéfini, elle crée le lien de SdifF vers le type prédéfini. Sinon, elle envoie un message sur l'erreur standart.

SdifMatrixTypeT* SdifMatrixTypeInsertTailColumnDef (SdifMatrixTypeT *MatrixType, char *NameCD)
permet d'ajouter une colonne à un type (toujours la dernière colonne).

void SdifPutMatrixType (SdifHashTableT *MatrixTypesTable, SdifMatrixTypeT* MatrixType)
permet d'ajouter un type de matrice dans une table.

SdifUInt4 SdifMatrixTypeGetNumColumnDef (SdifMatrixTypeT *MatrixType, char *NameCD)
renvoie la position de la colonne de nom NameCD. (0 si elle n'existe pas)

SdifColumnDefT* SdifMatrixTypeGetColumnDef (SdifMatrixTypeT *MatrixType, char *NameCD)
renvoie la définition de la colonne (numéro, nom) en fonction du nom.(NULL si introuvable)

SdifColumnDefT* SdifMatrixTypeGetNthColumnDef (SdifMatrixTypeT *MatrixType, SdifUInt4 NumCD)
renvoie la définition de la colonne (numéro, nom) en fonction du numero.(NULL si introuvable)

SdifMatrixTypeT* SdifGetMatrixType (SdifHashTableT *MatrixTypesTable, SdifSignature Signature)
renvoie le type de matrice en fonction de la Signature. Renvoie NULL si le type est introuvable. Attention, si Signature est la signature d'un type prédéfini, SdifGetMatrixType(SdifF->MatrixTypeTable,Signature) renvoie NULL si le lien avec entre SdifF et gSdifPredefinedType n'a pas été mis à jour.

Exemple: On souhaite complèter le type de matrice des FOFs par une nouvelle colonne appelée NewCol et ensuite créer un type exclusif.

    {
      SdifMatrixTypeT *MtrxTFOF, *NewMtrxT;

      /* Mise à jour du lien sur le type prédéfini '1FOF' si nécessaire*/
      MtrxTFOF = SdifTestMatrixType(SdifF, '1FOF');
      /* Maintenant le type de matrice '1FOF' est directement accessible par SdifF et
       * non uniquement par gSdifPredefinedTypes. */

      /* ajout d'une nouvelle colonne (en complétion) */
      if (MtrxTFOF)
        SdifMatrixTypeInsertTailColumnDef(MtrxTFOF, "NewCol");
      else ; /* un message d'erreur a été dans ce cas envoyer par SdifTestMatrixType*/

      /* Création d'un type exclusif (le premier caractère de la signature doit donc
       * être 'E') */
       NewMtrxT = SdifCreateMatrixType('ENMT', NULL);
       /* il n'y a pas de type prédéfini pour 'ENMT' donc le deuxième argument est NULL */
       SdifPutMatrixType (SdifF->MatrxTypesTable, NewMtrxT); /* ajout à la base */
       /* ajout des colonnes */
       SdifMatrixTypeInsertTailColumnDef(NewMtrxT, "Col1");
       SdifMatrixTypeInsertTailColumnDef(NewMtrxT, "Col2");
       SdifMatrixTypeInsertTailColumnDef(NewMtrxT, "Col3");
       SdifMatrixTypeInsertTailColumnDef(NewMtrxT, "Col4");
    }

#### Opérations sur FrameTypesTable

Les opérations sur les types de frames sont similaires dans leur fonctionnement à celle des types de matrices.

SdifFrameTypeT* SdifCreateFrameType (SdifSignature FrameSignature, SdifFrameTypeT *PredefinedFrameType)

SdifFrameTypeT* SdifTestFrameType (SdifFileT* SdifF, SdifSignature FrameSignature)

SdifFrameTypeT* SdifFrameTypeInsertTailComponent (SdifHashTableT *MatrixTypesTable, SdifFrameTypeT *FrameType, SdifSignature MatrixSignature, char *NameC)

void SdifPutFrameType (SdifHashTableT *FrameTypesTable, SdifFrameTypeT *FrameType)

SdifUInt4 SdifFrameTypeGetNumComponent (SdifFrameTypeT *FrameType, char *NameCD)

SdifComponentT* SdifFrameTypeGetComponent (SdifFrameTypeT *FrameType, char *NameCD)

SdifComponentT* SdifFrameTypeGetNthComponent (SdifFrameTypeT *FrameType, SdifUInt4 NumC)

SdifFrameTypeT* SdifGetFrameType (SdifHashTableT *FrameTypesTable, SdifSignature FrameSiganture)

#### Opérations sur StreamIDsTable

Il n'y a pas encore de fonctions de haut niveau pour remplir et interroger la table des StreamID (StreamIDsTable).

SdifStreamIDT* SdifCreateStreamID (SdifUInt4 NumID, char *Source, char *TreeWay)
permet de créer un pointeur sur un objet de type StreamIDT.

Exemple:

    /* dans le cas d'un TreeWay pour champ (non fichier)*/
    void ConsOneStreamID(SdifFileT *SdifF,
    		     int        NumID,
    		     char      *PatchType,
    		     int        NumPatch,
    		     char      *ObjType,
    		     int        NumObj,
    		     int        NbSubObj,
    		     float      StartTime,
    		     float      EndTime)
    {
      SdifStreamIDT* StreamID;
      char TreeWay[512];

      sprintf(TreeWay, "%s/%d/%s/%d/%d/%s/%f", PatchType, NumPatch, ObjType,
    		    NumObj, NbSubObj, StartTime, EndTime);
      StreamID = SdifCreateStreamID(NumID, "Chant", TreeWay);

      SdifHashTablePut(SdifF->StreamIDsTable, &(StreamID->NumID), 1, StreamID);
    }

    /*pour recuperer un StreamID il faut utiliser la fonction SdifHashTableGet*/
    {
      SdifStreamIDT* StreamID;

      StreamID = (SdifStreamIDT*) SdifHashTableGet (SdifF->StreamIDsTable, &NumID, 0);
      /* le troisième argument n'est pas utilisé, car la table est indexée directement
         par des entiers (création de la table avec l'option eInt4).
       */
    }

### Opérations sur les éléments temporaires de SdifFileT*

SdifFrameHeaderT* SdifFSetCurrFrameHeader (SdifFileT *SdifF, SdifSignature Signature, SdifUInt4 Size, SdifUInt4 NbMatrix, SdifUInt4 NumID, SdifFloat8 Time)
permet de donner des valeurs à chaque champ de l'entête de frame temporaire de SdifF.
Exemple: `SdifSetCurrFrameHeader(SdifF, '1FOB', 3, NumID, 1.0);`

SdifMatrixHeaderT* SdifFSetCurrMatrixHeader (SdifFileT *SdifF, SdifSignature Signature, SdifDataTypeET DataType, SdifUInt4 NbRow, SdifUInt4 NbCol)
permet de donner des valeurs à chaque champ de l'entête de matice temporaire de SdifF.
Exemple: `SdifSetCurrMatrixHeader(SdifF, '1FOF', eFloat4, NbFofs, 7);`

SdifOneRowT* SdifFSetCurrOneRow (SdifFileT *SdifF, void *Values)
recopie la mémoire pointée par Values en fonction de l'entête de matrice courante.
Exemple:

    #define NbCols = 10;
    {
      float t[NbCols] = { 1., 2., 3., 4., 5., 6., 7., 8., 9., 0.};

      SdifSetCurrMatrixHeader(SdifF, 'mtrx', eFloat4, 1, NbCols);
      SdifSetCurrOneRow      (SdifF, (void*) t);
    }

On connait la taille de la mémoire à recopier par le type de donnée (ici: eFloat4) et le nombre de colonnes (ici: NbCols). Il faut que le type de donnée de la matrice courante corresponde avec la taille d'un élément de t. Si t est composé de float sur 4 bytes, alors on doit avoir eFloat4. Si t est composé de double float sur 8 bytes, alors c'est eFloat8.
En général, les données d'un programme ne se présente pas sous cette forme et il faut réaliser une transposition lors des transfert de Sdif à un programme. Le programme Diphone Ircam a un bon exemple de lecture avec transposition automatique, généralisée pour tout type de matrice.

SdifOneRowT* SdifFSetCurrOneRowCol (SdifFileT *SdifF, SdifUInt4 numCol, SdifFloat8 Value)
permet de donner la valeur Value dans la ligbe de matrice temporaire de SdifF à la colonne numCol (0CurrMtrxH->NbCol).

SdifFloat8 SdifFCurrOneRowCol (SdifFileT *SdifF, SdifUInt4 numCol)
recupère la valeur stockée à la colonne numCol de la ligne temporaire. C'est un SdifFloat8 donc un double!!

SdifFloat8 SdifFCurrOneRowColName (SdifFileT *SdifF, SdifMatrixTypeT *MatrixType, char *NameCD)
idem que la fonction précédente mais en utilisant le type de la matrice et le nom de la colonne.

SdifSignature SdifFCurrSignature (SdifFileT *SdifF)
renvoie la signature temporaire de Chunk ou de Frame.

SdifSignature SdifFCleanCurrSignature (SdifFileT *SdifF)
met à 0 tous les bits de la signature temporaire.

SdifSignature SdifFCurrFrameSignature (SdifFileT *SdifF)
renvoie la signature temporaire du dernier Frame lu ou du prochain à écrire.

SdifSignature SdifFCurrMatrixSignature (SdifFileT *SdifF)
renvoie la signature temporaire de la dernier matrice lue ou de la prochaine à écrire.

SdifOneRowT* SdifFCurrOneRow (SdifFileT *SdifF)
renvoie la ligne temporaire de SdifF.

SdifUInt4 SdifFCurrNbCol (SdifFileT *SdifF)
renvoie SdifF->CurrMtrx->NbCol, nombre de colonnes de la matrice en cours de traitement.

SdifUInt4 SdifFCurrNbRow (SdifFileT *SdifF)
renvoie SdifF->CurrMtrx->NbRow, nombre de lignes de la matrice en cours de traitement.

SdifUInt4 SdifFCurrNbMatrix (SdifFileT *SdifF)
renvoie SdifF->CurrFramH->NbMatrix, mombre de matrices du frame courant.

SdifUInt4 SdifFCurrID (SdifFileT *SdifF)
renvoie SdifF->CurrFramH->NumID, index de l'objet du frame courant.

SdifFloat8 SdifFCurrTime (SdifFileT *SdifF)
renvoie SdifF->CurrFramH->Time.

## Opérations sur le fichier binaire

### Ecriture

size_t SdifFWriteGeneralHeader (SdifFileT *SdifF)
écrit sur le fichier 'SDIF' puis 4 bytes à 0.

size_t SdifFWriteAllASCIIChunks (SdifFileT *SdifF)
écrit tous les chunks ASCII. C'est à dire: les tables de names values, les types créés ou complétés, et les Stream ID. Il faut donc au préalable avoir rempli complétement les tables avant de la lancer. Cette fonction de peut donc pas être executer une 2nd fois durant une écriture.

size_t SdifFWriteMatrixHeader (SdifFileT *SdifF)
Après avoir donner une valeur à chaque champ de SdifF->CurrMtrxH gràce à la fonction SdifFSetCurrMatrixHeader, SdifFWriteMatrixHeader écrit toute l'entête de la matrice. Cette fonction réalise aussi une mise à jour de SdifF->CurrOneRow, tant au niveau de l'allocation mémoire que du type de données.

size_t SdifFWriteOneRow (SdifFileT *SdifF)
Après avoir donner les valeurs à chaque case de SdifF->CurrOneRow à l'aide de SdifFSetCurrOneRow ou de SdifFSetCurrOneRowCol (suivant que l'on possède déjà un tableau flottant ou respectivement une méthode pour retrouver une valeur de colonne), SdifFWriteOneRow écrit 1 ligne de matrice suivant les paramètres de SdifF->CurrMtrxH.

size_t SdifFWriteFrameHeader (SdifFileT *SdifF)
Après avoir donner une valueur à chaque champ de SdifF->CurrFramH gràce à la fonction SdifFSetCurrFrameHeader, SdifFWriteFrameHeader écrit toute l'entête de frame. Lorsque la taille est inconnue au moment de l'écriture, donner la valeur _SdifUnknownSize. Ensuite, compter le nombre de bytes écrit dans le frame et réaliser un SdifUpdateChunkSize avec la taille calculée.

size_t SdifFWritePadding (SdifFileT *SdifF, size_t Padding)
Cette fonction permet en fin d'écriture de matrice d'ajouter le Padding nécessaire. Il faut cependant avoir la taille de ce Padding. On utilise SdifFPaddingCalculate(SdifF->Stream, SizeSinceAlignement) où SizeSinceAllignement est un `size_t` désignant le nombre de bytes qui sépare la position actuelle d'écriture avec une position connue où le fichier est aligné sur 64 bits (en général, c'est la taille de la matrice en cours d'écriture: NbRow*NbCol*DatWitdh).

void SdifUpdateChunkSize (SdifFileT *SdifF, size_t ChunkSize)
execute un retour fichier de ChunkSize bytes et l'écrit, donc on écrase la taille du chunk ou du frame. Dans le cas où le fichier est stderr ou stdout, l'action n'est pas réalisée.

    /* L'exemple suivant essaye de montrer l'ordonnancement des appels
    de fonction. Biensûr ce code devrait être plus modulaire. En effet,
    il devrait y avoir une fonction par niveau structurel d'écriture:
    une(plus) fonction(s) d'écriture de matrice, une(plus fonction(s)
    d'écriture de frame...
    */
    #include "sdif.h"

    void main(void)
    {
      SdifFileT   *SdifF;
      SdifUInt4    NbMatrix = 3;
      SdifUInt4    NumID = 0;
      SdifFloat8   Time = 0.0;
      SdifFloat4   TabValue[] = {1,2,3,4,5,6,7};
      SdifFloat4  *pTabValue;
      size_t       SizeFrameW;
      size_t       SizeMatrixW;

      pTabValue = TabValue; /* pour permettre le cast par pointeur */

      SdifGenInit("SdifTypes.STYP");

      SdifF = SdifOpenFile("NewFile.sdif",      eWriteFile);

      /* remplir les tables NameValues, MatrixTypesTable,
       * FrameTypesTable et StreamIDsTable.
       */
      [ ..... ]

      /* écriture de l'entête */
      SdifFWriteGeneralHeader (SdifF);
      /* écriture des chunks ASCII */
      SdifFWriteAllASCIIChunks (SdifF)

      /****FRAME HEADER*****/
      /* Mise à jour le l'entête de frame à écrire */
      SdifSetCurrFrameHeader (SdifF, '1FOB', _SdifUnknownSize, NbMatrix, NumID, Time);
      /* écriture de l'entête de frame */
      SizeFrameW = SdifFWriteFrameHeader (SdifF);

      /****FIRST MATRIX***/
      /* Mise à jour le l'entête de matrice à écrire : 1 ligne, 1 colonne*/
      SdifSetCurrMatrixHeader (SdifF, '1FQ0', eFloat4, 1, 1);
      /* écriture de l'entête de frame */
      SizeMatrixW = SdifFWriteMatrixHeader (SdifF);

      /* Mise à jour de la ligne-buffer de SdifF
       * La largeur des données est conservée par le eFloat4 de l'entête de matrice*/
      SdifSetCurrOneRow (SdifF, (void*) pTabValue);
      /* écriture de la ligne */
      SizeMatrixW += SdifFWriteOneRow (SdifF);
      /* Si on a d'autres lignes à écrire alors
       * on répette SdifSetCurrOneRow et SizeMatrixW += SdifFWriteOneRow...
       */

      /* écriture du Padding en fin de matrice et ajout de la taille de la matrice écrite
       * à la taile du frame.
       */
      SizeMatrixW += SdifFWritePadding(SdifF,
                           SdifFPaddingCalculate(SdifF->Stream, SizeMatrixW))
      SizeFrameW += SizeMatrixW;

      /** MATRIX 2 & 3 **/
      [. 2 matrices à écrire
       .
       .]

      /* pas de padding en fin de frame car on est déjà aligné */

      /* la taille écrite ne doit pas compter la signature et la taille===> -8 */
      SizeFrameW -= 8;
      SdifFUpdateChunkSize(SdifF, SizeFrameW);

      SdifCloseFile(SdifF);

      SdifGenKill();
    }

### Lecture

Remarque: En lecture, on a toujours une avance sur la signature des chunks ou des frames. Ceci permet d'orientée la lecture suivant le type de données: chunk ou frame.

int SdifFGetSignature (SdifFileT *SdifF, size_t *NbCharRead)
lit 4 bytes, les considère comme une signature qui est placée dans SdifF->CurrSignature, incrémente NbCharRead du nombre de bytes lus et renvoie le dernier caractère lu convert en int (-1 si erreur).

size_t SdifFReadGeneralHeader (SdifFileT *SdifF)
lit l'entête du fichier, c'est à dire 'SDIF' puis 4 bytes. affiche un message en cas de non reconnaissance du format.

size_t SdifFReadAllASCIIChunks (SdifFileT *SdifF)
Cette fonction permet de lire tous les Chunk ASCII qui se trouveraient en début de fichier juste après l'entête générale. Elle s'arrête lorsqu'elle ne reconnaît pas la signature de chunk comme un ASCII Chunk. Cette signature est donc normalement celle d'un frame. Elle est stockée dans SdifF->CurrSignature. Il n'est donc pas nécessaire de la relire.

size_t SdifFReadMatrixHeader (SdifFileT *SdifF)
Cette fonction lit une entête de matrice signature incluse. Elle vérifie le type de matrice, le champ DataType. Toute les données se trouvent stockées dans SdifF->CurrMtrxH. La plupart de ses champs sont directement accessible par les fonctions indépendantes du mode d'ouverture du fichier. Elle effectue une mise à jour de l'allocation mémoire de SdifF->CurrOneRow en fonction des paramètres de l'entête de matrice. Ainsi, on est normalement près pour lire chaque ligne de la matrice courrante.

size_t SdifFReadOneRow (SdifFileT *SdifF)
Cette fonction permet de lire 1 ligne de matrice. Les données lues sont stockées dans SdifF->CurrOneRow (jusqu'à une prochaine lecture d'entête de matrice qui réinitialise ses paramètres).

size_t SdifFReadFrameHeader (SdifFileT *SdifF)
Cette fonction lit l'entête d'un frame à partir de la taille et jusqu'au temps. Donc elle ne lit pas la signature mais donne à SdifF->CurrFramH->Signature la valeur de SdifF->CurrSignature. La lecture doit se faire avant, avec SdifFGetSignature.

size_t SdifSkipMatrix (SdifFileT *SdifF)
Cette fonction permet de passer une matrice toute entière entête incluse. Elle est utile lorsque qu'un frame contient plus de matrices que le programme lecteur n'en connaît. Il peut ainsi les passer pour retomber sur un autre frame.

size_t SdifSkipMatrixData (SdifFileT *SdifF)
Cette fonction permet de passer une matrice mais après la lecture de l'entête. On s'en sert lorsque le type de matrice est mauvais, inconnu, non interprétable par le programme lecteur.

size_t SdifSkipFrameData (SdifFileT *SdifF)
Cette fonction à le même sens que SdifSkipMatrixData mais pour les frames. Il faut donc pour l'utiliser avoir au préalable lu la signature et l'entête.

size_t SdifFReadPadding (SdifFileT *SdifF, size_t Padding)
Cette fonction permet de lire le Padding en fin de matrice. l'utilisation classique de cette fonctin est:
`SizeR = SdifFReadPadding(SdifF, SdifFPaddingCalculate(SdifF->Stream, SizeR));`
où SizeR est la taille en bytes lue depuis le début de la matrice, c'est à dire NbRow*NbCol*DataWith. En réalité, pour que SdifFPaddingCalculate fonctionne, il est seulement nécessaire que SizeR soit le nombre de bytes qui s'épare la position actuelle dans le fichier et un byte, repère d'allignement sur 64 bits.

## Conversions

La librairie possède deux fonctions permettant de faire des conversions de fichiers SDIF binaire vers du texte ou l'inverse.

size_t SdifTextToSdif (SdifFileT *SdifF, char *TextStreamName)
converti un fichier SDIF ouvert en lecture (eReadFile) en un fichier texte pseudo-SDIF de nom TextStreamName.

size_t SdifToText (SdifFileT *SdifF, char *TextStreamName)
converti un fichier texte pseudo-SDIF de nom TextStreamName en un fichier SDIF binaire de non SdifF->Name. Le fichier doit avoir été ouvert en écriture (eWriteFile).

# Ecriture d'un fichier pseudo-SDIF texte

La structure d'un fichier texte pseudo-SDIF est identique à celle des fichier binaire. Les différences sont que:

- il s'agit d'une suite de champs écrits en caractère ASCII, séparés par un séparateur (espaces, tabulations, retours chariot),
- tous les frames sont placés entre les signatures 'SDFC' et 'ENDC' (Start Data Frame Chunk et End Chunk),
- le fichier doit se terminer par 'ENDF' (End File),
- aucune taille de chunk ou de frame n'est à spécifier,
- les champs index, nombre de colonnes, de lignes, de matrices et le type de données sont des entiers écrits en caractères ASCII,
- les types de données sont:
  - 32 pour eFloat4,
  - 64 pour eFloat8.

## Exemple de fichier pseudo-SDIF

Dans cette exemple, sont représentés les possibilités des fichier pseudo-SDIF. On y trouve 2 NVTs placées au début. Ensuite, on a un chunk de déclaration de types, où le type de matrice '1CHA' est complèté pour avoir 6 cannaux, un type de matrice et un de frame sont créés en mode exclusif. Ensuite on a un chunk de déclaration d'objet, le premier StreamID rappelle la grammaire d'un StreamID, le second est un StreamID pour Chant et le troisième est un StreamID d'un autre type. Enfin, on a 2 frames qui portent sur les objets déclarés.

    SDIF

    1NVT
    {
      NameTable       General;
      SdifTextAuthor  Dupont;
      Date            15/JAN/1998;
    }

    1NVT
    {
      NameTable       Synthese;
      SamplingRate    44100.  ;
      EndTime         5.00;
      ChantMaxNbSubObjs   5;
    }

    1TYP
    {
      1MTD 1CHA {Channel5, Channel6}
      1MTD ENMT {a, b, c}

      1FTD ENFT
        {
          1FOF Formants;
          ENMT UntitledMatrix;
        }
    }

    1IDS
    {
      0  TypeDeTreeWay:TreeWay;
      1  Chant:Patch0/1/FOB/1/5/0./5.;
      2  ProgA:A/B/C/D/ENFT/1./5.;
    }

    SDFC

    1FOB	2	1	0.
      1FQ0	32	1	1
    	110.
      1FOF	32	5	7
    	650.	1	80.	0.002	0.05	0.004	0.
    	1080.	0.5012	90.	0.002	0.05	0.004	0.
    	2650.	0.4467	120.	0.002	0.05	0.004	0.
    	2900.	0.3982	130.	0.002	0.05	0.004	0.
    	3250.	0.0795	140.	0.002	0.05	0.004	0.
      1CHA	32	5	6
            1.	0.	0.      0.      0.      1.
    	0.	1.	0.	0.	0.	1.
    	0.	0.	1.	0.	0.	1.
    	0.	0.	0.	1.	0.	1.
    	0.	0.	0.	0.	1.	1.

    ENFT	2	2	1.
      1FOF	32	5	7
    	400.	1.	40.	0.002	0.05	0.004	0.
    	800.	0.3163	80.	0.002	0.05	0.004	0.
    	2600.	0.2512	100.	0.002	0.05	0.004	0.
    	2800.	0.2512	120.	0.002	0.05	0.004	0.
    	3000.	0.0501	120.	0.002	0.05	0.004	0.
      ENMT	32	4	3
    	1.	2.	3.
    	4.	5.	6.
    	7.	8.	9.
    	10.	11.	12.

    ENDC
    ENDF

Pour convertir un fichier pseudo-SDIF en fichier binaire SDIF avec l'utilitaire tosdif sous unix:
tosdif -i MyFoB.sdif.txt -o MyFoB.sdif