octobre
2010
Cet article fait suite au premier et deuxième billets dans lesquels nous avons vu comment créer un objet de la classe TagLib::File. Cet objet utilise les fonctions de la bibliothèque taglib, écrite en C, afin d’accéder aux tags de fichiers audio. Dans ce billet, nous verrons comment obtenir les valeurs des tags et comment modifier un tag.
Jouer avec les tags
Accéder au tag
La structure tgFileData contient un champ tag qui permettra d’accéder au tag du fichier. Il a été initialisé avec la valeur NULL. La fonction file_tag retourne la valeur de ce champ après l’avoir complété au besoin.
file_tag(VALUE self)
{
tgFileData *d;
TagLib_Tag *tgTag;
Data_Get_Struct(self, tgFileData, d);
if (d->tag==NULL)
{
tgTag=taglib_file_tag(d->file);
if (tgTag==NULL)
rb_raise(eBadTag, "Bad tag");
d->tag=tgTag;
}
return d->tag;
}
La macro Data_Get_Struct permet d’accéder à la structure tgFileData de notre objet. Si le champ tag n’est pas rempli, on récupère sa valeur grâce à la fonction taglib_file_tag de la bibliothèque taglib. Une exception est levée dans le cas où cette dernière fonction faillirait. La valeur du champ est finalement retournée.
Obtenir le titre d’une piste audio
Afin d’obtenir le titre contenu dans un tag, une méthode title est ajoutée à la classe TagLib::File, dans la fonction Init_taglib2.
La fonction file_get_title implémente la méthode TagLib::File#title
file_get_title(VALUE self)
{
VALUE str;
str=rb_str_new2(taglib_tag_title(file_tag(self)));
taglib_tag_free_strings;
return str;
}
La valeur du titre est obtenu grâce à la fonction taglib_tag_title qui prend la valeur du champ tag de la structure interne tgFileData. La fonction rb_str_new2 transforme cette valeur obtenue sous la forme d’un pointeur sur chaîne de caractères en un objet ruby de la classe String. La mémoire allouée par taglib pour cette opération est libérée avant de retourner l’objet.
Modifier le titre d’une piste audio
La méthode TagLib::File#title=, prenant un argument, est ajoutée.
La fonctionfile_set_title utilise la bibliothèque taglib pour modifier le tag. Une nouvelle fois, la macro StringValuePtr est utilisée pour convertir une variable du type VALUE vers le type char *. Une fonction implémentant une méthode accessible depuis ruby doit obligatoirement retournée une valeur du type VALUE. J’ai choisi ici de retourner la chaîne de caractère passée en argument.
file_set_title(VALUE self, VALUE title)
{
taglib_tag_set_title(file_tag(self), StringValuePtr(title));
return title;
}
Il me semble que traditionnellement les méthodes du type variable= retourne l’argument alors que les méthodes du type set_variable retourne l’objet. La troisième possibilité étant de retourner nil.
Sauvegarder les modifications
Après avoir modifié le titre à l’aide de la méthode vue au précédent paragraphe, il faut écrire cette modification dans le fichier audio.
La méthode TagLib::File#save permet cette écriture. La méthode retourne true si l’opération est réussie ou false.
file_save(VALUE self)
{
tgFileData *data;
Data_Get_Struct(self, tgFileData, data);
if (taglib_file_save(data->file))
{
return Qtrue;
}
else
{
return Qfalse;
}
}
La fonction taglib_file_save réalise l’écriture après avoir accédé au champ file de la structure interne de notre objet ruby.
Tester le code
Voici le contenu du fichier taglib2.c
#include <ruby.h>
#include <taglib/tag_c.h>
typedef struct
{
TagLib_File *file;
TagLib_Tag *tag;
const TagLib_AudioProperties *audio;
} tgFileData;
VALUE mTagLib;
VALUE eBadPath, eBadFile, eBadTag, eBadAudioProperties;
VALUE cFile;
static void
free_tgFileData(tgFileData *d)
{
taglib_file_free(d->file);
free(d);
}
TagLib_Tag *
file_tag(VALUE self)
{
tgFileData *d;
TagLib_Tag *tgTag;
Data_Get_Struct(self, tgFileData, d);
if (d->tag==NULL)
{
tgTag=taglib_file_tag(d->file);
if (tgTag==NULL)
rb_raise(eBadTag, "Bad tag");
d->tag=tgTag;
}
return d->tag;
}
VALUE
file_init(VALUE self, VALUE path)
{
rb_iv_set(self, "@path", path);
return self;
}
VALUE
file_new(VALUE self, VALUE path)
{
TagLib_File *tgFile;
tgFileData *data;
VALUE rbData;
VALUE argv[1];
tgFile=taglib_file_new(StringValuePtr(path));
if (tgFile==NULL)
rb_raise(eBadPath, "Bad path");
data=ALLOC(tgFileData);
data->file=tgFile;
data->tag=NULL;
data->audio=NULL;
rbData=Data_Wrap_Struct(cFile, 0, free_tgFileData, data);
argv[0]=path;
rb_obj_call_init(rbData, 1, argv);
return rbData;
}
VALUE
file_save(VALUE self)
{
tgFileData *data;
Data_Get_Struct(self, tgFileData, data);
if (taglib_file_save(data->file))
{
return Qtrue;
}
else
{
return Qfalse;
}
}
VALUE
file_get_title(VALUE self)
{
VALUE str;
str=rb_str_new2(taglib_tag_title(file_tag(self)));
taglib_tag_free_strings;
return str;
}
VALUE
file_set_title(VALUE self, VALUE title)
{
taglib_tag_set_title(file_tag(self), StringValuePtr(title));
return title;
}
void
Init_taglib2()
{
mTagLib=rb_define_module("TagLib");
eBadPath=rb_define_class_under(mTagLib, "BadPath", rb_eException);
eBadFile=rb_define_class_under(mTagLib, "BadFile", rb_eException);
eBadTag=rb_define_class_under(mTagLib, "BadTag", rb_eException);
eBadAudioProperties=rb_define_class_under(mTagLib, "BadAudioProperties", rb_eException);
cFile=rb_define_class_under(mTagLib, "File", rb_cObject);
rb_define_singleton_method(cFile, "new", file_new, 1);
rb_define_method(cFile, "initialize", file_init, 1);
rb_define_method(cFile, "save", file_save, 0);
rb_define_method(cFile, "title", file_get_title, 0);
rb_define_method(cFile, "title=", file_set_title, 1);
}
Après avoir recompilé, nous pouvons tester les méthodes ajoutées dans cet article.
$ irb
> require './lib/taglib2.rb'
=> true
> t=TagLib::File.new('Musique/01 - Blue Bird.mp3')
=> #<TagLib::File:0xb74ca844>
> t.title
=> "Blue Bird"
> t.title='toto'
=> "toto"
> t.title
=> "toto"
> t.save
=> true
Conclusion
Nous avons vu comment accéder au titre d’une piste audio. Accéder aux autres propriétés (auteur, album, etc) n’est pas plus difficile. Je ne développerai pas cet aspect. Les sources du code complet seront cependant mis à votre disposition lors du dernier article. Le prochain article devrait parler de l’aspect documentation.