README
file says, the configure
script must be run with the CFLAGS="-ggdb3 -O0" LDFLAGS="-ggdb3"
environment variables, to be able to start from the build directory.PulseAudio modules are required to have two functions:
pa__init(pa_module*m)
and pa__done(pa_module*m)
, which are called when the module is loaded and, respectively, unloaded (either by PulseAudio or because it has finished whatever it was doing). Other than these functions, PulseAudio recognises a few others that provide some information about the module (such as the author or the description). However, these functions are not generally implemented directly, but rather created through the following macros defined in pulsecore/module.h
:PA_MODULE_AUTHOR(s) PA_MODULE_DESCRIPTION(s) PA_MODULE_USAGE(s) PA_MODULE_VERSION(s) PA_MODULE_DEPRECATED(s) PA_MODULE_LOAD_ONCE(b)
pulsecore/module.h
also contains the pa_module
struct definition, which is explained on the Module API wiki page. One particularly relevant field of the structure is the userdata
field, because it's the only way to carry information from pa__init
to pa__done
. This is why modules will generally define a userdata struct
that contains all the relevant module data, and that is used by pa__done
to free whatever memory is allocated in pa__init
. This struct
is also used to pass data to core hooks that handle various events.Another important field in the
pa_module
structure is the core
field. This contains a pa_core
struct
, which is defined in pulsecore/core.h
and is described on the Core API wiki page. This field is particularly relevant because it is used when creating hooks.Creating and freeing hook slots is done through two functions defined in
pulsecore/hook-list.h
:pa_hook_slot* pa_hook_connect(pa_hook *hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data); void pa_hook_slot_free(pa_hook_slot *slot);
The core hook types are stored in the
hooks
array in pa_core
, which is indexed according to the pa_core_hook
enum defined in pulsecore/core.h
. As an example, the hook that is called after a card has been created is referred to by m->core->hooks[PA_CORE_HOOK_CARD_PUT]
.pa_hook_priority_t
is another enum
defined in pulsecore/hook-list.h
and can currently take the values PA_HOOK_EARLY
, PA_HOOK_NORMAL
or PA_HOOK_LATE
. However, these are just integers and the call can be adjusted to control the order in which hooks are run (e.g. PA_HOOK_LATE+10
will run after PA_HOOK_LATE
).The
pa_hook_cb_t
is a function pointer defined in pulsecore/hook-list.h
, with the following definition:typedef pa_hook_result_t (*pa_hook_cb_t)( void *hook_data, void *call_data, void *slot_data);and the last parameter is used for whatever user data needs to be passed from the module to the hook callback function.
Putting all this together, the following module is able to detect and log whenever a new card is plugged in:
#ifdef HAVE_CONFIG_H #include <config.h> #endif #include <pulse/proplist.h> #include <pulse/xmalloc.h> #include <pulsecore/card.h> #include <pulsecore/core.h> #include <pulsecore/log.h> #include <pulsecore/module.h> #include "module-desktop-notifications-symdef.h" PA_MODULE_AUTHOR("Ștefan Săftescu"); PA_MODULE_DESCRIPTION("Integration with the Desktop Notifications specification."); PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_LOAD_ONCE(TRUE); struct userdata { pa_hook_slot *card_put_slot; }; static pa_hook_result_t card_put_cb(pa_core *c, pa_card *card, void* userdata) { pa_log_info("Card detected: %s.", pa_proplist_gets(card->proplist, PA_PROP_DEVICE_DESCRIPTION)); return PA_HOOK_OK; } int pa__init(pa_module*m) { struct userdata *u; m->userdata = u = pa_xnew(struct userdata, 1); u->card_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_CARD_PUT], PA_HOOK_LATE, (pa_hook_cb_t) card_put_cb, u); return 0; } void pa__done(pa_module*m) { struct userdata *u; pa_assert(m); if (!(u = m->userdata)) return; if (u->card_put_slot) pa_hook_slot_free(u->card_put_slot); pa_xfree(u); }