[Sub0] - 26/05/03

Le sample WAV

 

                  


L'amplitude
: Un sample est composé d'une courbe continue ayant une valeur bi-polaire. Le 1er élément d'un son est l'amplitude: C'est le point le plus élevé (et le plus bas) de la courbe. Plus l'amplitude est élevée, plus le son est fort, bruyant. L'unité de grandeur de l'amplitude est le décibel (dB). "C'est une mesure logarithmique donnant le degré d'amplification d'une vibration." Nous n'irons pas plus loin dans la description du décibel, puisque ce n'est pas indispensable dans la programmation. L'amplitude est digitalisée avec l'ADC de la carte son. Par exemple, en 8 bits, l'amplitude possède une résolution de 256 valeurs. En 16 bits, 65536 valeurs, etc... Il existe aussi le 24 et 32 bits. Plus la résolution est élevée, plus l'échantillon est proche du son original. Dans les images ci-dessus, l'amplitude digitalisée est illustrée en vert.


La fréquence: En bleu, c'est la fréquence d'échantillonnage, le nombre de valeurs définissant l'amplitude pour une seconde d'enregistrement. Ainsi 44100 Hz signifie 44100 échantillons pour une seconde de son mémorisé. Plus la fréquence du son est élevée, plus le son est aigu. Plus la fréquence d'échantillonnage est élevée, meilleure est la qualité du sample, plus les données digitales sont proches de l'original.


Le débit: On peut calculer le "débit" (ko/s) d'un sample avec ces paramètres: L'amplitude (format 8 ou 16 bits), le mode (mono ou stéréo) et la fréquence. Par exemple, en 8 bits mono 44100Hz, cela nous donne pour une seconde d'enregistrement: 44100 octets (1 octet=8 bits). En stéréo, c'est le double. En 16 bits stéréo, c'est le quadruple. Ainsi, avec la qualité du CD audio (16 bits stéréo 44,1 kHz), on obtient 176400 octets par seconde. A partir de la taille du fichier (donnée par fileSize), et les caractéristiques du sample, il est facile de calculer le temps total en secondes (ou ms) de lecture d'un sample...


Le format PCM: C'est le format de fichier "standard" pour les samples, car les données sont brutes, c'est-à-dire qu'elles ne sont ni modifiées, ni compressées depuis l'ADC. Le fichier possède une en-tête de 44 octets, permettant de connaître le type du sample: Son format, sa fréquence, le nombre de voies, etc... En 8 bits, la valeur de l'amplitude est non signée, et en 16 bits, l'amplitude est signée. Cela complique un peu les choses, par exemple, pour convertir un sample 16 bits en 8 bits, il ne suffit pas de diviser la valeur de l'amplitude par 256, il faut aussi penser à soustraire (ou à ajouter) ce décalage. Cette manip est expliquée un peu plus bas dans ce document... Le format WAV possède aussi d'autres particularités. Par exemple, voici l'en-tête d'un sample 16 bits mono 44,1 kHz:

CONST HEADER:ARRAY[0..43]OF BYTE=(
$52,$49,$46,$46,
$00,$00,$00,$00, $57,$41,$56,$45, $66,$6D,$74,$20,
$10,$00,$00,$00, $01,$00,
$01,$00, $44,$AC,$00,$00, $88,$58,$01,$00,
$02,$00,$10,$00, $64,$61,$74,$61, $00,$00,$00,$00);

TYPE THEAD=RECORD
Tag1       : ARRAY[1..4]OF CHAR; 
{ 00..03  Constante "RIFF"       }
Size1      : LONGINT;             { 04..07  Filesize-8             }
Tag2       : ARRAY[1..14]OF CHAR;
{ 08..21  Constante "WAVEfmt..." }
Mode       : WORD;                { 22..23  Mono or Stereo         }
Freq       : LONGINT;             { 24..27  Frequence (Hz)         }
BytePerSec : LONGINT;             { 28..31  Freq*NbrByte           }
NbrByte    : WORD;                { 32..33  (Format div 8)*Mode    }
Format     : WORD;                { 34..35  8 or 16 bits           }
Tag3       : ARRAT[1..4]OF CHAR;  
{ 36..39  Constante "data"       }
Size2      : LONGINT;             { 40..43  Filesize-116           }
END;



Accéder aux octets - Méthode:
Le type Longint possède 4 octets, tandis qu'un Word, en possède 2.

TYPE LongRec=RECORD lo,hi:WORD;END;
     WordRec=RECORD lo,hi:BYTE;END;

LongRec permet de décomposer un LONGINT en 2 WORD (lo & hi), et WordRec permet de décomposer un WORD en 2 BYTES (lo & hi). Par exemple, pour obtenir les 4 octets du longint size:

VAR s1,s2,s3,s4:BYTE;
    size:LONGINT;

BEGIN
s1:=WordRec(LongRec(size).lo).lo;
s2:=WordRec(LongRec(size).lo).hi;
s3:=WordRec(LongRec(size).hi).lo;
s4:=WordRec(LongRec(size).hi).hi;

Pour l'opération inverse, je fais la somme des octets multipliés par leur poids:

size:=(s4*$1000000)+(s3*$10000)+(s2*$100)+s1;
END;



L'ordre des données: Après les 44 octets de l'en-tête, viennent les données. Les données ont un ordre bien défini. Dans le cas d'un sample 8 bits mono, c'est 1 seul octet par sample, donc les données se suivent normalement. Pour un sample 8 bits stéréo, ce sont 2 octets par sample, l'octet de la voie de gauche, puis l'octet de la voie de droite: L,R, L,R, L,R, etc... Dans le cas d'un sample 16 bits mono, ce sont 2 octets par sample, l'octet de poids faible, puis l'octet de poids fort. Par exemple pour une donnée qui vaudrait 15000, nous aurions: $98 $3A (les octets sont toujours inversés). Dans le cas d'un sample 16 bits stéréo, ce sont 4 octets par sample; Deux octets pour la voie de gauche, et deux pour la voie de droite: L,L,R,R, L,L,R,R, L,L,R,R, etc...


Le format des données: Le sample 8 bits (mono ou stéréo) possède des données non-signées, c'est-à-dire que le point (l'amplitude) le plus bas vaut zéro, le point du milieu vaut 127, et le point le plus haut vaut 255. Avec un sample 16 bits, les choses sont différentes, les données sont signées: Le point le plus bas vaut -32268, le point du milieu vaut zéro, et le point le plus au vaut 32767. Voici une illustration de l'amplitude d'un sample 16 bits: A gauche, le format d'origine des données (signées), et à droite, les valeurs lorsque les données seront converties en données non-signées:

Pour pouvoir travailler sur les données 16 bits, par exemple pour modifier le volume du sample, ou le mixer avec un autre, etc... les données 16 bits signées doivent-être converties en données 16 bits non-signées avant le traitement, puis reconverties en données signées après le traitement afin d'être prêtes à la lecture. Voici donc la méthode que j'utilise pour transformer une donnée 16 bits signée en non-signée. La variable nommée "donnée" est du type Integer (avec Delphi) ou Longint (avec TP):

Voici l'opération de conversion (signée
-> non-signée):
donnee:=donnee-$8000;
IF(donnee<0)THEN donnee:=donnee+$10000;

Voici l'opération inverse (non-signée
-> signée):
donnee:=donnee+$8000;
IF(donnee>=$10000)THEN donnee:=donnee-$10000;

Difficile de faire plus simple! 

Attention! Toutes ces explications ne sont valables que pour le format PCM, car il existe d'autres types de compression pour les samples WAV! Cependant, le format PCM est le plus utilisé pour le traitement des samples...
Les exemples proposés dans ce tutoriel, ont été réalisés et testés avec Delphi 6 (voir sommaire).