WRITING XMMS PLUGINS ---a small tut----- 1 .......... Introduction 2 .......... The plugin interface 2.1 ........... The plugin structures 2.2 ........... Other things in the header file 2.2.1 ............ AFormat 2.2.2 ............ xmms_get_gentitle_format() 2.2.3 ............ Effect plugin communications 3 .......... Saving configurations 3.1 ........... Opening a configuration file 3.2 ........... Reading and writing data to the configuration file 3.3 ........... Closing up 4 .......... More about visualization plugins 4.1 ........... GDK/GTK 4.2 ........... The visualization structure 4.2.1 ............ xmms_session 4.2.2 ............ Channels wanted 4.2.3 ............ Disable the plugin 4.2.4 ............ Playback 4.2.5 ............ Data rendering 4.3 ........... Rendering the data 5 .......... In closing 1. Introduction This is a quick and dirty tutorial on how to start writing xmms plugins. I will get into more details about the visualization plugins. All the others are pretty new to me too! So this isn't a complete tut. I'm writing this because there is so little documentation on the subject out there. I discovered that when I wanted to write a simple plugin that would detect audio clipping (when the sound is to loud). I will give you a tour troughout the xmms plug in interface. And help you on your way to bigger and better things. All these things can also be found just reading header files though. Do what you prefer. 2. The xmms plugin interface 2.1 The plugin structures When writing a plugin, you should include the plugin.h header file provided by xmms in your source file: #include To allow people to program plugins, xmms provides for each kind of plugin (visualization, general, I/O and effects) a data structure. These structures have some things in common and some specific features. You can find the definitions of these structures in the header file: /usr/include/xmms/plugin.h You can see 5 different plugin structures: - OutputPlugin - EffectPlugin - InputPlugin - GeneralPlugin - VisPlugin Each have several fields in common. I will explain them to you. void *handle; This is filled in by xmms itself. So you shouldn't pay much attention to it. Initialize this as NULL. char *filename; This is also of xmms' concern. Make this NULL too. char *description; As stated in the comments, this is the description that will show up in the preferences box. void (*init) (void); This is a pointer to the init function. It should be a function returning nothing and taking nothing as arguments. This function will be called when the plug in is initially loaded. That is, when you initialize it from the preferences. This is a good place to initialize global variables or build a GTK window or something like that. void (*about) (void); This is a pointer to a function that will display a typical "about" box. If you don't care about that you can make this NULL. No one will cry. Once again the function has no return value or arguments. I'm not going to repeat this again. void (*configure) (void); This points to a function that will prompt the user with a GUI in which he can fill in all kinds of options to configure your plugin. I will explain to you how you can easily safe your configurations in the xmms config files later on. void (*cleanup)(void); Except for the OutputPlugin structure, all the structures have this field. It is once again a pointer to a function. The function is responsible for cleaning up anything that might require some final cleaning up (Destroying windows, closing files ...). It is called when xmms is closed or when the plugin is disabled. All other fields are more specific. I won't go in to them all here! 2.2 Other things in the header file Besides those structures, you can see some other stuff as well. I'll explain them here. Although most of them are pretty obvious and I don't know more about them than you do. 2.2.1 AFormat This enum defines several different audio formats. 8-bit, 16-bit and whatnot. It's used mainly by the effect and I/O plugins. 2.2.2 xmms_get_gentitle_format() gchar *xmms_get_gentitle_format(void); This function returns a string (a pointer to gchar... which is the GTK wrapper for a char *) which can be used in a call to xmms_get_titlestring(). 2.2.3 Effect plugin communications There are two functions provided so that output plugins can communicate with effect plugins. The first function is: EffectPlugin *get_current_effect_plugin(void); It returns a pointer to the data structure of the currently loaded effects plugin. The other function is effects_enabled(). This functions returns TRUE if an effect plugin is enabled, and FALSE if not. 3. Other usefull functions You can use many functions of xmms (all the code ... I guess...). But one thing that will come in very handy when writing plugins is a way to safe your configurations. Luckily, xmms provides a way to add certain configuration options to it's configuration file (by default stored in ~/.xmms/config). 3.1 Opening a configuration file All the interesting functions are listed in the file: /usr/include/xmms/configfile.h To use these functions you have to #include the header file: #include To open a configuration file. You can use either of these two functions: ConfigFile *xmms_cfg_open_file(gchar * filename); ConfigFile *xmms_cfg_open_default_file(void); xmms_cfg_open_default_file() will open a new file with the default filename (~/.xmms/config). xmms_cfg_open_file() will open a file of your choice. It will return NULL on an error (when you have the wrong permissions or something like that). ConfigFile *xmms_cfg_new(void); This function will allocate memory for a new ConfigFile. Normally, the open functions take care of that. 3.2 Reading and writing data to the configuration file The configuration file of xmms is divided into sections. Each section has data for a particular part of the program. For example, each plugin uses it's own section (normally). The data itself is located using "keys". Each key can have a certain value. You can give keys values in several data types. You can write (and read) strings, integers, booleans (true or false), floats or doubles into the configuration file. This reading functionality is provided by the following functions: gboolean xmms_cfg_read_string(ConfigFile * cfg, gchar * section, gchar * key, gchar ** value); gboolean xmms_cfg_read_int(ConfigFile * cfg, gchar * section, gchar * key, gint * value); gboolean xmms_cfg_read_boolean(ConfigFile * cfg, gchar * section, gchar * key, gboolean * value); gboolean xmms_cfg_read_float(ConfigFile * cfg, gchar * section, gchar * key, gfloat * value); gboolean xmms_cfg_read_double(ConfigFile * cfg, gchar * section, gchar * key, gdouble * value); All five of these functions do basicly the same, but with other data types. The first argument is a pointer to the ConfigFile obtained with the "open" functions described above. The "section" is a string with the name of a section. The "key" can be anything (except NULL). And the "value" is pointer to a variable which will store the data read in from the file. These functions will return FALSE if there was a problem (for example, the key was not found, no such section, some argument was NULL ...). The writing functionality is provided by these functions: void xmms_cfg_write_string(ConfigFile * cfg, gchar * section, gchar * key, gchar * value); void xmms_cfg_write_int(ConfigFile * cfg, gchar * section, gchar * key, gint value); void xmms_cfg_write_boolean(ConfigFile * cfg, gchar * section, gchar * key, gboolean value); void xmms_cfg_write_float(ConfigFile * cfg, gchar * section, gchar * key, gfloat value); void xmms_cfg_write_double(ConfigFile * cfg, gchar * section, gchar * key, gdouble value); The section argument can be a string of your choice (not NULL), if the section doesn't exist, it will be created. I think it's best using a constant defined somewhere, so you don't end up with two different sections when you make some typo's. The key can be anything you want it to be (not NULL), it will also be created when it doesn't exist. The "value" is now not a pointer (except for the string technically ... but you know) but just an actual value that will be written into the file. If you ever want to remove a key from the file you can use this function: void xmms_cfg_remove_key(ConfigFile * cfg, gchar * section, gchar * key); The arguments are pretty straight forward. 3.3 Closing up. When you're done writing your data. You should write away the file before closing it, so that the changes are saved into the actual file! You can do that with these functions: gboolean xmms_cfg_write_default_file(ConfigFile * cfg); gboolean xmms_cfg_write_file(ConfigFile * cfg, gchar * filename); The first one writes to the default file, the other one writes to a file of your choice. On error these functions return FALSE. When all this is done, you need to free the ConfigFile with this function: void xmms_cfg_free(ConfigFile * cfg); 4. More about visualization plugins Well, the reason I learned about the xmms plugin interface was basicly to write that little clipping indicator. Which I wrote as a visualization plugin. So that's the part I know a little bit more of! 4.1 GDK/GTK XMMS uses GTK-1.2, and you pretty much don't have much choice but to use it as well. Not a problem for me, I knew shit about GUI programming anyway. People are writing a version of xmms which uses GTK-2 I believe. Check the web site of xmms! (Allthough the GTK-2 version isn't written by the developers of xmms and it has a different name bla bla) For more info on GTK you can check out the website: www.gtk.org 4.2 The visualization structure I will get more in depth on the fields of the plugin structure for visualization which I haven't covered in chapter 2: - int xmms_session - int num_pcm_chs_wanted - int num_freq_chs_wanted - void (*disable_plugin)(struct _VisPlugin *) - void (*playback_start)(void) - void (*playback_stop)(void) - void (*render_pcm)(gint16 pcm_data[2][512]) - void (*render_freq)(gint16 freq_data[2][256]) 4.2.1 xmms_session This is a session ID used by xmms. You can generally set this to 0. Xmms will take care. 4.2.2 Channels wanted The two fields num_pcm_chs_wanted and num_freq_chs_wanted specify how many channels the visualization plugin wants to examinate of respectively PCM data samples or frequency data. This will become more clear later on. 4.2.3 Disable the plugin The field "disable_plugin" should be set to NULL. XMMS will take care of this. You'll need this function to disable the plugin. For example when someone closes the visualization window. You can just call the function with a pointer to the visualization structure of the plugin. This will trigger the same actions as if you were disabling the plugin from within the preferences window. 4.2.4 Playback When the playback starts, the playback_start function is called, and when it stops, the playback_stop function will be called. You can use this to do some work to initialize your plugin. Or clean up afterwards. Note that these two will not be called on a songchange! playback_stop() will be called when the user presses stop or when the playlist reaches it's end. 4.2.5 Data rendering If these functions are not NULL (and the respective requested channels are > 0) they will be called repetitively troughouth the duration of playing. Each time having as an argument a chunck of data which can be used to render the visualizations. 4.3 Rendering the data XMMS gives you the choice to render raw PCM data (sample values) or to render frequency data (256 values, for "each" frequency. I THINK). Each time the PCM data renderer function is called, you get as an argument a two dimensional array containing 512 samples in each dimension. Well that is, if you specified that you want two channels. If you only want one, you have to assign "1" to the "num_pcm_chs_wanted" field in the structure. The frequency rendering is pretty much the same, but you're not dealing with raw samples anymore, there has been some preprocessing done on them. 5. In closing Well... that's all I can think of right now. I hope this tutorial was helpful or cleared a few things up for you! It is not complete at all. And I urge everyone who can to expand it, correct it or anything else! If any mistakes are made about language I'm sorry about that. English is not my mother language, but it's what people on the net use, so I do too in the hope I'll reach more people. For more check out www.xmms.org There's a link to a pretty cool visualization tut with nice source code included. (c) Aaron Lenoir 2004 (excuse the 1337 sp33k ;)) If you chose to e-mail me about this tutorial please include the word "xmmsplugintut" in the subtject. Copyright notice: Feel free to redistribute, change, expand or delete this file. If you chose to change this tutorial, adding value to it, please be so kind to mention me. Aside from that, I couldn't care less.