Image logo.gif


SDIF

Le format de fichier

Analyse/Synthèse

Dominique Virolle



Principe de SDIF

L'idée générale de SDIF est de stocker les informations liées au traitement du signal et plus particuliairement du son, dans des fichiers, suivant un format commun à tous les types de données. Ainsi, il est possible de stocker des résultats ou des paramètres d'analyses, de synthèses...

Des Matrices

Le format a comme première spécification le stockage des données sous forme de matrices ou chaque colonne représente un champ de structure, et chaque ligne un élément de cette structure. Une structure que l'on peut représenter sous forme de matrice est dite dans ce document "simple".

Image Matrix.gif

Ainsi, pour une structure Filtre composée de trois champs "fréquence", "amplitude", et "largeur de bande", des éléments de Filtre sont stockés comme une matrice de trois colonnes et de autant de lignes qu'il y a d'éléments.

Filtre fréquenceamplitudelargeur de bande
Filtre1- - -
Filtre2- - -
FiltreN- - -

Des Frames

La seconde spécification de SDIF est que les matrices sont regroupées dans des frames. Il y a plusieurs types de frames : les bancs de filtres, les bancs de fofs... Un frame s'adresse à un objet contenant plusieurs sous-objets de structure(s) simple(s). Ainsi, un frame BancDeFiltre permet de représenter un ensemble de Filtres. Une structure associée à un frame est dite "composée".

Image Frame.gif

Notion Temporelle

A chaque modification dans le temps d'un paramètre d'un objet est associé un bloc frame SDIF pour le type de l'objet. Le fichier SDIF est globalement une succession de frames ordonnés dans le temps.

Image DataChunk.gif

Des Chunks

Un fichier SDIF se compose de plusieurs chunks dans l'ordre :

Image FileLevel.gif

Il devrait y avoir une Time Table mais son format et sa position n'ont pas encore été décidés.

BigEndian

Quelque soient les données d'un fichier SDIF ou le type de matériel informatique qui écrit ou lit des fichiers SDIF, toutes les données sont écrites en BigEndian IEEE et alignées sur 8 bytes.


Le Format

File Header

L'entête d'un fichier SDIF se compose de 2 champs :

"SDIF"4 bytesSignature SDIF
<champ vide>4 bytesnon encore défini (== 00000000 hexa)

Informations Table Chunk

Informations Table Header

L'entête d'un chunk d'une table d'informations est codée sur 8 bytes :

"1NVT"4 bytesSignature Informations Table
<Chunk Size>4 bytes Taille du Chunk sans compter l'entête.
(Padding inclus et par défault ffffffff hexa)

Informations Table Data

La table d'informations permet d'avoir des informations générales comme l'auteur du fichier, la version de SDIF, le nombre de cannaux... Les données de ce chunk sont en ASCII.

Les données commencent par '{' et finissent par '}'. Chaque information est sous la forme <Nom> <Valeur> ';' . Il est nécessaire d'avoir au moins un caractère d'espacement entre le nom et la valeur (et non des caractères nuls '\0' car c'est une partie ASCII).

Exemple de Informations Table Data:

{
  numChannels   6   ;
  SDIFVersion   1.0.0.alpha;
  MonProgrammeParametreSpecial  integer4  ;
}

La grammaire associée est :

<Info Table Data>    := '{' <Info declaration> | ... '}'
  <Info declaration> := [<space chars>] <Name> 
                                 <space chars> <Value> [<space chars>]';'
    <space chars>    := <space> | ...
      <space>        :=  ' ' | '\t' | '\n' | '\f' | '\r' | '\v'
    <Name>           := ASCII string
    <Value>          := ASCII string

Padding

L'aspect ASCII de la table d'informations commence au '{' et fini au '}'. Mais après le '}', il y a un Padding qui peut varier de 0 à 7 bytes. Chaque bytes du Padding est mis à '\0'. Le Padding permet d'aligner le fichier sur 64 bits. Ainsi le chunk suivant débute aligné.

Types Declarations Chunk

Il est possible dans SDIF de compléter les types de matrices ou de frames existant (cf : Predefined Types) ou d'en créer de nouveaux. Si un types existe déjà, on considère qu'une déclaration sur ce type est une complétion. S'il n'existe pas, alors c'est une création.

Cependant, pour un type donné, il ne peut y avoir qu'une seule déclaration dans un fichier SDIF. C'est à dire que l'on ne peut pas compléter un type une deuxième fois et on ne peut pas créer un nouveau type puis le compléter.

Comme pour les tables d'informations, les données de déclarations de types sont en ASCII. Par contre, il ne peut y en avoir qu'un.

Important : Les créations sont vivement déconseillées puiqu'il s'agit d'un mode exclusif par rapport à une application. De plus, les noms de types créés doivent avoir 'E' comme premier caractère. Ainsi, un type exclusif peut être ajouter à la base des types prédéfinis en remplaçant le 'E' par un digit. Les fichiers utilisant le type exclusif est ainsi toujours lisible (pas de conflits de déclaration de type).

Types Declarations Header

L'entête d'un chunk de déclaration de types est codée sur 8 bytes :

"1TYP"4 bytesSignature Types Declarations
<Chunk Size>4 bytes Taille du Chunk sans compter l'entête
(Padding inclus et par défault ffffffff hexa)

Types Declarations Data

La partie donnée du chunk Types Declarations est en ASCII. Elle commence donc par '{' et fini par '}'. Une donnée est soit une déclaration de type de matrice, soit une déclaration de type de frame. Comme un type de frame est un ensemble de matrices, sa déclaration dépend des déclarations des types de matrices contenues. Deux mots clés permettent de savoir s'il s'agit d'une déclaration d'un type de matrice ou d'un frame. "1MTD" pour matrice, "1FTD" pour frame.

Type de Matrice

La déclaration d'un type de matrice commence donc par "1MTD". On a ensuite le nom du type de matrice sur 4 caractères ASCII dont le premier est un chiffre indiquant la version du type si c'est une complétion ou 'E' si c'est une création. Ensuite, on définie comme un tableau en C les noms des champs de la structure représentée. Les champs indiques les entêtes de colonnes.

Exemples :

   1MTD EFIL {frequence, amplitude, largeurdebande}
   1MTD  1TYP { Champ1, Champ2, Champ3 }
   1MTD2TYP{Champ21,Champ22,Champ23}


EFIL est une création, 1TYP et 2TYP sont des complétions.

Comme les tailles de "1MTD" et du nom du type sont fixes, il n'est pas nécessaire d'avoir des espacements (toutefois, c'est préférable).

Les noms de champs définissent l'ordre des colonnes dans les matrices. Celui-ci ne peut pas être modifier sans créer un nouveau type. Contrairement aux structures C, les champs ne sont pas typés. Ceci vient du fait que les données sont toujours des floattants sur 4 bytes ou sur 8 bytes (cf. Matrice Data).

La Complétion de Matrice :

Lorsque le type de matrice est prédéfini (il existe comme format dans SDIF), une déclaration sur ce type entraine le mode complétion. Celle-ci consiste seulement à ajouter de nouvelles colonnes (champs) aux matrices (aux structures simples). Ainsi si 1FIL est prédéfini dans SDIF avec "fréquence", "amplitude", et "largeur de bande", 1MTD 1FIL {saliance, correction} ajoute deux nouvelles colonnes aux matrices (la 4 et la 5). Mais aucune modification des colonnes prédéfinies n'est possible.

Type de Frame

La déclaration d'un type de frame commence par "1FTD". Ensuite on a le nom du type de frame (ne doit pas exister comme type de matrice) sur 4 caractères ASCII dont le premier digit indique la version du type si c'est une complétion ou 'E' si c'est une création. Le bloc de définition commence par '{' et fini par '}'. Chaque donnée du type de frame est : un nom de type de matrice existant, un nom de champ de structure frame. A la fin d'une donnée de type de frame, on a un ';'.

Exemple (supposant 1FIL et 1TYP existant) :

    

Création : mode exclusif
    1FTD EFIB
     {
       1FIL  filtres;
       1TYP  exemple1types;
     }


Complétion
    1FTD 1FIB
     {
       2TYP exemple2types
     }

EFIB et 1FIB ne représentent pas le même type de frame même s'ils ont les
mêmes 2 premières matrices, et que les 3 lettres significatives sont 'FIB'.

Ainsi, on défini EFIB comme un frame de 2 matrices. Comme pour les matrices, si le type de frames est prédéfini, alors on complète le type de frame par des matrices supplémentaires. Ainsi, si 1FIB est prédéfini comme dans l'exemple précédent, si on déclare 1FTD 1FIB {2TYP exemple2types}, alors on ajoute une nouvelle matrice à 1FIB (exemple1types de type 1TYP est toujours là).

Types Declarations Grammaire

<Types Declaration Data> := '{' <Matrix or Frame Declaration> | ... '}'
  <Matrix or Frame Declaration> :=  <Matrix Declaration> 
                                       | <Frame Declaration>
    <Matrix Declaration> := 1MTD [space chars] <Matrix Name> [space chars]
                                   '{' <Col Names > <One Col Name> '}'
      <Matrix Name> := 4 chars (32bits)
      <Col Names> := [<(N-1) Col Names>]
        <(N-1) Col Names> := <One Col Name Not Last> | ... 
          <One Col Name Not Last> := <One Col Name> ','
      <One Col Name> := [space chars]<string>[space chars]
         <string> := ASCII chars
    <Frame Declaration> := 1FTD [space chars] <Frame Name> [space chars]
                                   '{' <Frame Component> | ... '}'
      <Frame Name>  :=  4 chars (32bits)
      <Frame Component> := [spaces] <Matrix Name>
                                 [spaces] <Frame Component Name>[spaces] ';'
         <Frame Component Name> := <string>


Exemple de déclaration de types (ici ils sont complétés):

1TYP <ChunkSize>
{
  1MTD 1FIL {frequency, amplitude, banwidth}
  1MTD 1CHA {channel1, channel2}

  1FTD FIB
    {
      1FIL filtersparameters;
      1CHA filterschannels;
    }


  1MTD 1FOF {frequence, amplitude, banwidth, tex, debatt, atten, phase}
  1MTD 1FQ0 {fondamentalfq}

  1FTD 1FOB
    {
      1FQ0 pitch;
      1FOF fofsparameters;
      1CHA fofschannels;
    }
}

Le chunk de déclarations de types est optionnel.

Comme pour le chunk d'informations, le bloc données du chunk de type est suivi d'un Padding.

Stream IDs Table Chunk

Stream IDs Table Header

L'entête d'un chunk de la table de d'ID est aussi codée sur 8 bytes :

"1IDS"4 bytesSignature Stream IDs Table
<Chunk Size>4 bytes Taille du Chunk sans compter l'entête
(Padding inclus et par défault ffffffff hexa)

Stream IDs Table Data

La table d'ID permet d'avoir des informations sur les objets sur lesquels les frames vont s'appliquer. Un ID est un entier représentant un objet particulier. Deux objets ont toujours deux ID différents, même s'ils se différencient déjà par leurs types. Ainsi un ID est une clé absolue pour un objet par rapport à un fichier SDIF.

Les données commencent par '{' et finissent par '}'. Chaque information est sous la forme <ID> <Source ou Destination>: <TreeWay> ';' . Les données sont en ASCII.

Exemple de Stream IDs Table Data:

{
  1   MonProg:Groupe1/1/FIB/0/12/500./3./80.;
  2   MonProg:Groupe1/1/FIB/1/5/500./3./80.;
  3   MonProg:Groupe1/1/FIB/2/8/500./3./80.;
  4   TonProg:FOB/"Fofbank"/4/4/2;
}

La signification et la construction de TreeWay dépendent de la Source, mais rien n'est prédéfini. La Source doit être un nom de programme, ou un nom de méthode de calcul, d'analyse, de synthèse... Elle est utile pour le programme qui va lire les données et non par celui qui les écrit. C'est la raison pour laquelle on parle de Source ou de Destination. TreeWay peut donner des informations sur des liens entre objet. Une composante dans les TreeWay semble obligatoire : il doit y avoir le nom du type de l'objet dans le TreeWay (sans le numéro de version pour qu'un lecteur SDIF puisse faire une tentative de lecture sur un type de frame de version inconnue). La table des ID est en quelque sorte le moyen de déclarer des objets qui seront modifiés dans le temps via les frames.

La grammaire associée est :

<ID Table Data>            := '{' <ID declaration> | ... '}'
  <ID declaration>         := [<space chars>] <ID> 
                                     <space chars> <Souce ou Destination> ':'
			             [<space chars>] <TreeWay> ';'
    <space chars>          := <space> | ...
      <space>              :=  ' ' | '\t' | '\n' | '\f' | '\r' | '\v'
    <ID>                   := ASCII digits
    <Source ou Destination>:= ASCII string
    <TreeWay>              := ASCII string (définition variable)
         Les composants de TreeWay sont séparés par le symbol '/'.

Comme pour les précédents chunk, le Stream IDs Chunk est aligné sur 8 bytes par Padding.

Frames Chunks

Frame

Frame Header

Il faut pour l'entête du frame, avoir le nom du type, la taille du frame (Somme des matrices), le nombre de matrices, l'ID de l'objet de la structure composée du frame et le temps qui designe l'instant de la mise en place des données codé en double précision (8 bytes).

Le nombre de matrices d'un frame peut être inférieur au nombre de matrices du type de frame. Le frame peut contenir seulement les N premières matrices du type : 0 < N <= NbMatriceFrameType. si on considère le type de frame 1FOB (cf. Types Declarations Grammaire), on peut avoir un frame portant uniquement sur le "pitch" et sur les "fofsparameters". Les "fofschannels" ne sont alors pas défini : N == 2. Mais, il n'est pas possible d'avoir un frame 1FOB avec les matrices "pitch" et "fofschannels" car l'ordre des matrices doit suivre l'ordre donnée dans la déclaration de type (ou du type prédéfini).

Remarque : Il ne doit pas y avoir d'"états". C'est à dire qu'a chaque frame, toutes les connaissances sur l'objet indexé doivent apparaitre pour le temps donné. On doit pouvoir ainsi donner toutes les informations d'un objet à la lecture d'un seul frame pour un temps donné; inutile de lire les frames de temps inférieurs.

<Nom du type de Frame>4 byteschar[4]
<Frame Size>
(entête incluse)
4 bytesint 4
<Nombre de Matrices N>4 bytesint 4
<ID de l'objet concerné>4 bytes int 4
<Temps>8 bytesfloat 8

Frame Data

Les données d'un frame sont simplement une succession de N matrices. Il n'y a pas de Padding pour les frames car ils sont obligatoirement alignés par le fait que l'entête est sur 24 bytes donc alignée sur 8 bytes et que chaque matrice sera alignée.

Matrice 1Alignée sur 8 bytes
...Alignée sur 8 bytes
Matrice NAlignée sur 8 bytes

Matrix

Matrix Header

L'entête d'une matrice commence par sa signature. Elle doit correspondre avec la signature déclarée dans le type de frame en fonction de la position de la matrice dans le frame.

Après le nom du type de matrice, il doit y avoir le code du format de donnée :

Ensuite, on a le nombre de lignes L qui dépend du nombre d'éléments de la structure simple contenus dans l'objet (ID) de la structure composée. Les éléments de la structure simple sont indexés uniquement par le numéro de la ligne dans la matrice.

Après L, le nombre de colonnes C. Celui-ci a la même propriété que N du niveau frame. C'est à dire que la matrice contient les C premières colonnes du type de matrice.

Les 3 données "type de données (32 ou 64), L et C sont écrites dans un fichier SDIF en float (4 bytes).

Nom du type de Matrice4 byteschar
Largeur des données (bits)4 bytesfloat 4
Nombre de lignes L4 bytesfloat 4
Nombre de colonnes C4 bytesfloat 4

Matrix Data

La matrice est écrite ligne après ligne. Elle est suivit d'un Padding pour l'alignement sur 8 bytes.

Ligne1 (4 ou 8 bytes)*Cfloat 4 ou 8
...(4 ou 8 bytes)*Cfloat 4 ou 8
LigneL(4 ou 8 bytes)*Cfloat 4 ou 8
Padding0 ou 4 bytes '\0'

Exemple de Frame en texte

( ) : commentaire pour l'exemple.



'1FOB'  <FrameSize>  (N=)3   (ID=)0    (Time=)1.45


  (matrice 1 : pitch)
  '1FQ0'    (TypeDonnées=)32    (L=)1    (C=)1

       (frequency)
          164.


  (matrice 2 : fofsparameters)
  '1FOF'    (TypeDonnées=)32    (L=)5    (C=)7
        (frequency amplitude bandwidth    tex  atten   debatt phase)
	   609.       80.       78.     0.002   0.05    0.004   0.   (fof1)
	   1000.      53.9	88.     0.002   0.05    0.004   0.   (fof2)
	   2450.      18.	123.	0.002   0.05    0.004   0.   (fof3)
	   2700.      19.	128.    0.002   0.05    0.004   0.   (fof4)
	   3200.      6.1	138.    0.002   0.05    0.004   0.   (fof5)


  (matrice 3 : fofschannels)
  '1CHA'    (TypeDonnées=)32    (L=)5    (C=)1
        (channel1 channel2)
	   1.5       1.    (fof1)
	   1.5	     1.    (fof2)
	   1.5	     2.2   (fof3)
	   1.5	     0.5   (fof4)
	   1.5	     1.5   (fof5)

 A part les signatures, la taille du frame, le nombre de matrices N et ID,
 toutes les données sont des floats. Time est toujours en double précision.


Mots Reservés SDIF

Mots Information Table

SDIFAuthorauteur du fichier
SDIFLibraryVersionversion de la librairie SDIF
SDIFTypesVersionversion du format des types

Predefined Types

1 décembre 1997

Predefined Matrix Types

NomSignification
1FQ0Fréquence fondamentale ou excitation d'un banc de fofs
1FOFForme d'Onde Formantique
1CHAChannels
1RESFiltre Resonnant
1DISDistribution d'un processus aléatoire

1FQ0    {Frequency, Mode, Hit}
  Frequency : Fréquence fondamentale d'un banc de fofs >0. (Hz).
  Mode      : Mode d'excitation (0: Frequency, 1:Hit, 2:Both).
  Hit       : Excitation (Dirac) sur un temps précis 
              (0:pas d'excitation, 1: excitation).


1FOF	{Frequency, Amplitude, BandWidth, Tex, DebAtt, Atten, Phase}
  Frequency : Fréquence du fof  >0. (Hz).
  Amplitude : Amplitude de l'enveloppe du fof linèaire.
  BandWidth : Largeur de bande du Fof >0. (Hz).
  Tex       : Temps d'exitation >0.  (secondes).
  DebAtt    : Instant de début de l'atténuation de l'enveloppe >0. (secondes).
  Atten     : Durée de l'atténuation >0.  (secondes).
  Phase     : Phase de la sinusoïde du fof 0. à 2pi rad.


1CHA	{Channel1, Channel2, Channel3, Channel4}
  channelX : Amplitude linéaire sur le channel X   >0..
  Si il y a plus de 4 cannaux, il suffit d'avoir un chunk de déclaration de types
  et de faire une complétion de 1CHA :
     1MTD 1CHA {Channel5, Channel6}
  On peut ainsi ajouter autant de cannaux que l'on le souhaite.


1RES	{Frequency, Amplitude, BandWidth, Saliance, Correction}
  Frequency  : Fréquence du filtre résonnant.
  Amplitude  : Gain du Filtre.
  BandWidth  : Largeur de bande du Filtre >0. (Hz).
  Saliance   : pourcentage d'erreur des paramètres 0.à 100.
  Correction : paramètre de correction automatique du gain par rapport
        aux autres paramètres  0. à 1.


1DIS	{Distribution, Amplitude}
  Distribution : type de distribution
     (pas encore défini mais 0 signifie équi-répartie)
  Amplitude : variance du processus aléatoire (amplitude).
     Ce type peut-être compléter pour faire apparaître des moments
     d'ordre supérieur.

Predefined Frame Types

NomSignification
1FOBBank de fofs
1REBBank de filtres résonnants
1NOIBruit

1FOB
  {
    1FQ0  PitchModeHit;
    1FOF  Formants;
    1CHA  FormantsChannels;
  }
PitchModeHit : excitation du fofbank. 1 seule ligne par frame 1FOB.
Formants : paramètres des enveloppes de fof.
FormantsChannels : amplitude de sortie des fofs sur chaque canal.
  On pourrait avoir les cannaux dans 1FOF mais la complétion du
  type ne permetterait que d'augmenter le nombre de cannaux
  et non d'ajouter des paramètres liés à l'enveloppe.



1REB
  {
    1RES  Filters;
    1CHA  FiltersChannels;
  }
Filters : paramamètres des filtres.
Filterschannels :  amplitude de sortie des filtres sur chaque canal.
   Même remarque que pour fofschannels de 1FOB.



1NOI
  {
    1DIS  NoiseInfo;
  }
NoiseInfo : paramètres du bruit.


}

StreamID TreeWay pour Chant

Rappel: Le champ TreeWay d'un StreamID n'a pas de définition absolue. Son interprétation dépend du champ Source qui représente le type de TreeWay qui le suit. La seule mise en forme prévue est que le TreeWay doit avoir l'allure d'une URL. <

La librairie Chant sait interpréter des StreamID dont le champ Source est 'Chant' et où le champ TreeWay suit ce formalisme:
<PatchType>/<NumPatch>/<ObjType>/<NumObj> /<NbSubObjt>/<StartTime>/<EndTime>[/"<SoundFileName>"]
([] signifie ici "éventuellement, Obj: Objet, Num: Numéro, Nb: Nombre").

Description de chaque élément du TreeWay

Exemple

Prenons un exemple de type de patch très général.
Image PatchExemple.gif
Chaque type d'objet de Chant y est représenté. Ce type de patch n'est qu'un exemple et n'existe pas comme type de patch prédéfini.

On remarque qu'il y a 2 bancs de filtres. Ceci va faire intervenir le numéro de l'objet (à droite des noms sur le schéma). En effet, le banc de filtre dont le numéro est 1 correspond au banc qui ne filtre que le banc de FOFs. Le banc 2 correspond à celui qui filtre tous les objets. On pourra avoir par exemple les Stream ID suivant pour remplir le patch.

1IDS
{
  145   Chant:PatchTypeExemple/1/FOB/1/23/0./5.;
  7     Chant:PatchTypeExemple/1/REB/1/45/0./5.;
  32    Chant:PatchTypeExemple/1/NOI/1/0/0./6.;
  12    Chant:PatchTypeExemple/1/SND/1/0/0./6./"./snd/file.sf.rs";
  122   Chant:PatchTypeExemple/1/REB/2/85/0./6.;
}

Les NumID sont idépendants des TreeWay. Ils doivent seulement être unique. Le nombre de sous-objet, les temps de fins et de début sont aussi indépendant pour chaque objet. L'objet de NumID 7 est le banc de filtres 1 du patch. C'est donc ce lui qui ne filtre que le banc de FOFs. L'objet de NumID 122 est le banc de filtres 2 qui filtre le banc de FOFs, le bruit et le fichier son.
Avoir un TreamID supplémentaire :
123 Chant:PatchTypeExemple/1/REB/3/85/0./6.;
ne permet pas d'avoir un troisième banc de filtres, car un type de patch est figé.
De plus, s'il manque l'un des composants du patch, alors ce patch n'apparaitra pas à la synthèse. Un patch doit être complet pour être pris en compte dans la synthèse.

Actuellement, ces types de patch sont disponibles :

Image Patch0.gif Image Patch1.gif Image Patch2.gif Image Patch3.gif Image Patch4.gif Image Patch5.gif Image Patch6.gif Image Patch7.gif Image Patch8.gif Image Patch9.gif Image Patch10.gif

Le Stream ID SND de Patch2 contient le nom d'un fichier son à filtrer. Cependant, il n'y a ni matrice, ni frame associé à cet objet.

 Exemple de Stream ID pour un Patch1 :
  0	Chant:Patch1/1/FOB/1/4/0./1.;
  1	Chant:Patch1/1/REB/1/5/0./1.;
  2	Chant:Patch1/1/NOI/1/0/0./1.;   // un bruit n'a pas de sous objets.
si on a un autre Patch1: 3 Chant:Patch1/2/FOB/1/3/2./5.; 4 Chant:Patch1/2/REB/1/8/2./5.; 5 Chant:Patch1/2/NOI/1/0/2./5.; Patch2: 6 Chant:Patch2/1/NOI/1/0/0./4.; 7 Chant:Patch2/1/REB/1/5/0./5.; 8 Chant:Patch2/1/SND/1/0/0./4./"filename";