SED-P.NET

Speex / Delphi

mardi 4 septembre 2007, par Bech ()


(Pour speex et dotnet, c’est par là)

Dur dur de trouver des infos sur le net pour utiliser le codec Speex sous Delphi triste.

Ayant eu du mal, je vous propose donc d’en profiter en utilisant le fichier joint (en bas de page). Il s’agit du projet que l’on trouve sur le site de speex : http://speex.org/software/. Il manque des fichiers dans la démo d’origine, j’ai donc fait les modifications pour que tout soit fonctionnel.

Zip - 708.7 ko
Speex delphi demo

Le fichier zip contient :

 Speex 1.0
— libspeex.dll (speex-1.2beta2)
— speex.pas
— le projet "Speex_Delphi_Demo" que l’on trouve sur le net malheureusement sans les fichiers précédents (Cet exemple utilise uniquement les fonctions de la libspeex version 1.0.5).

 Speex 1.2
— libspeex.dll (speex-1.2beta2)
— speex.pas (nouvelle version)
— le projet "Speex_Delphi_Demo" modifié.

version originale : delphi speex demo

Dernière version du fichier Speex.pas pour la libspeex >= v1.1

Ce fichier speex.pas permet d’utiliser la version 1.1 ou 1.2 de la libspeex. Elle permet donc entre autre les fonctions speex_encode_int et speex_decode_int ainsi que celles du préprocesseur tel que speex_preprocess.

Format des données pour speex

speex utilise, jusqu’a la version 1.0, un tableau de single pour traiter les données audio.

  1.  
  2. speex_encode: function(state: PSpeexState; in_:PSingle; bits: PSpeexBits): Integer cdecl;
  3. speex_decode: function(state: PSpeexState; bits: PSpeexBits; out_: PSingle): Integer cdecl;
  4.  

A partir de la version 1.1, on peut utiliser un tableau de type entier sur 16bits signé (SmallInt). Pour cela on fait appel aux fonctions :

  1.  
  2. speex_encode_int: function(state: PSpeexState; in_:PSmallInt; bits: PSpeexBits): Integer cdecl;
  3. speex_decode_int: function(state: PSpeexState; bits: PSpeexBits; out_: PSmallInt): Integer cdecl;
  4.  

Dans tout les cas, quand vous utiliser la taille d’une frame, il s’agit du nombre de valeur et non le nombre d’octet. Vous devez prendre cela en considération car sinon vous allez avoir des erreurs d’accès mémoire.

Explication : si une frame fait 160 en taille et que vous encoder en SmallInt, il vous faut deux fois plus d’octet ( sizeof(smallint) = 2 ) et 4 fois plus en Single.

Dans les exemple qui suivent, j’utilise des SmallInt donc vous verez parfois dans mon code des "encframe*2" pour obtenir une taille en octet.

Encodage avec speex

Pour commencer, en toute logique vous aurez besoin de quelques variables :

  1.  
  2. var
  3.     encbits: TSpeexBits;
  4.     encstate: PSpeexState;
  5.     encprestate: PSpeexPreprocessState;
  6.  
  7.     encframe: integer; // taille d'une frame
  8.  
  9.     buffBytes: array of byte// tampon pour l'encodage
  10.  
  11.     smpRt: integer// taux de capture
  12.  
  13.     q,i : Integer; // pour passer des paramètres lors de la config
  14.  

Ces variables sont donc initialisées :

  1.  
  2.     // Pour charger la DLL
  3.     Speex_Load_DLL;
  4.        
  5.     // Préparer une structure speex_bits
  6.     speex_bits_init(@encbits);
  7.  
  8.     // Préparer un structure pour l'encodage
  9.     encstate := speex_encoder_init(speex_lib_get_mode(SPEEX_MODEID_NB));
  10.  
  11.     q := 10// qualité d'encodage speex
  12.     smpRt := 8000// Taux de notre capture audio
  13.  
  14.     speex_encoder_ctl(encstate,SPEEX_SET_SAMPLING_RATE,@smpRt);
  15.     speex_encoder_ctl(encstate,SPEEX_SET_QUALITY,@q);
  16.     speex_encoder_ctl(encstate,SPEEX_SET_VBR_QUALITY,@q);
  17.  
  18.     i := 1; speex_encoder_ctl(encstate, SPEEX_SET_VBR, @i)// Active le mode VBR
  19.     i := 1; speex_encoder_ctl(encstate, SPEEX_SET_VAD, @i); // Active la détection de voix
  20.  
  21.     // Obtenir la taille d'une frame
  22.     speex_encoder_ctl(encstate,SPEEX_GET_FRAME_SIZE,@encframe);
  23.  
  24.     // Prépare la structure du préprocesseur
  25.     encprestate := speex_preprocess_state_init(encframe, smpRt);
  26.  
  27.     // Initialise le préprocesseur : DENOISE, AGC, VAD et DEREVERB
  28.     i := 1; speex_preprocess_ctl(encprestate, SPEEX_PREPROCESS_SET_DENOISE, @i);
  29.     i := 1; speex_preprocess_ctl(encprestate, SPEEX_PREPROCESS_SET_AGC, @i);
  30.     i := 1; speex_preprocess_ctl(encprestate, SPEEX_PREPROCESS_SET_VAD, @i);
  31.     i := 1; speex_preprocess_ctl(encprestate, SPEEX_PREPROCESS_SET_DEREVERB, @i);
  32.  

On pense de suite à prévoir la libération de ces variables dans une procédure style "OnClose" ou "Destroy" (Comme cela on est tranquile après...) :

  1.  
  2.    speex_preprocess_state_destroy(encprestate);
  3.    speex_bits_destroy(@encbits);
  4.    speex_encoder_destroy(encstate);
  5.  

Voila, maintenant je suppose que vous savez capture de l’audio et plaçer les échantillons dans un buffer du type "Array of smallint" (entier sur 16bit).

Dans ce premier exemple, buffer est donc un tableau de taille égale à une frame speex (variable encframe) :

  1.  
  2.   speex_preprocess(encprestate, @Buffer[0], nil);
  3.   speex_encode_int(encstate, @Buffer[0], @encbits);
  4.  
  5.   // sz contient le nombe d'octet à écrire / prévoir
  6.   sz := speex_bits_nbytes(@encbits);
  7.  
  8.   // On prépare la taille du tampon qui contiendra les données encodées
  9.   if length(buffBytes) < sz then SetLength(buffBytes,sz);
  10.  
  11.   //  sz2 contient le nombre d'octet écrit
  12.   sz2 := speex_bits_write(@encbits,@buffBytes[0],sz);
  13.  
  14.   // on vide l'état d'encodage
  15.   speex_bits_reset(@encbits);
  16.  
  17.   // il ne reste plus qu'a transmettre ou enregistrer le tableau buffBytes...
  18.  

Dans le code suivant, le buffer capturé est plus grand qu’une frame speex (un multiple de cette taille) et l’encodage va se faire sur plusieurs frames. size contient la taille total des données capturées.

  1.  
  2.   i := 0// on compte le nombre de frame déjà encodée
  3.   l := size;  // taille restante dans le buffer
  4.  
  5.   while l >= encframe do begin
  6.  
  7.     // Encode the sound data //
  8.     speex_preprocess(encprestate, @Buffer[(i*encframe)*2], nil);
  9.     speex_encode_int(encstate, @Buffer[(i*encframe)*2], @encbits);
  10.  
  11.     l := l - (encframe*2);
  12.     i := i + 1;
  13.   end;
  14.  
  15.     // sz contient le nombe d'octet à écrire / prévoir
  16.     sz := speex_bits_nbytes(@encbits);
  17.  
  18.     if length(buffBytes) < sz then SetLength(buffBytes,sz);
  19.  
  20.     //  sz2 contient le nombre d'octet écrit
  21.     sz2 := speex_bits_write(@encbits,@buffBytes[0],sz);
  22.  
  23.     //
  24.     speex_bits_reset(@encbits);
  25.  

Décodage de speex

Egalement quelques variables :

  1.  
  2. var
  3.   decbits: TSpeexBits;
  4.   decstate: PSpeexState;
  5.   decframe: integer// taille d'une frame
  6.   buffDec: array of SmallInt// décodage d'une frame
  7.  

Ces variables sont donc initialisées :

  1.  
  2.     // Pour charger la DLL
  3.     Speex_Load_DLL;     
  4.  
  5.   speex_bits_init(@decbits);
  6.   decstate := speex_decoder_init(speex_lib_get_mode(SPEEX_MODEID_NB));
  7.  
  8.   speex_decoder_ctl(decstate,SPEEX_GET_FRAME_SIZE,@decframe);
  9.  
  10.   SetLength(buffDec, decframe);
  11.  
  12.   speex_bits_reset(@decbits);
  13.  

Comme toujours on pense à prévoir la libération de ces variables :

  1.  
  2.   speex_bits_destroy(@decbits);
  3.   speex_decoder_destroy(decstate);
  4.  

Voila, on va maintenant décoder un buffer de taille size qui peu contenir une ou plusieurs frames :

  1.  
  2.   // On commence par lire la totalité des données encodées, zou...
  3.   speex_bits_read_from(@decbits,@buffer[0],size);
  4.  
  5.   // on prépare la taille du buffer en fonction de celle d'une frame
  6.   if decframe > length(buffDec) then SetLength(buffDec,decframe);
  7.  
  8.   repeat // tant qu'il y a des données à décoder
  9.  
  10.       i := speex_decode_int(decstate,@decbits,@buffDec[0]);
  11.  
  12.       if i = 0 then // une frame décodée
  13.           // moi je la met dans un stream...
  14.           AudioStream.Write(buffDec[0], (decframe*2));
  15.  
  16.   until i <> 0;
  17.  
  18.  

Répondre à cet article

1 Message

  • Speex / Delphi

    7 octobre 2007 09:20, par marc
    bonjour c’est très bien mais j’ai plus de 60 ans et je ne suis pas informaticien n’y a-t-il une méthode ou un logiciel pou simplement transformer les fichiers wave en spx pour un GPS si je vous les envoie pouvez-vous me les transformer mon email marco.polo47@free.fr merci d’avance

    Répondre à ce message

Creative Commons License
Cette création est mise à disposition sous un contrat Creative Commons .

Articles de cette rubrique


Dernières brèves


A visiter