PM123 supports four kinds of plug-ins: visual, decoder, output and filter. Plug-ins are Dynamic Linked Libraries (DLLs) which PM123 loads on start up.
Visual plug-ins are used to peek at the data currently being heard (or not) by the user through the output plug-in and visually produce data from it back to the user.
Decoder plug-ins are used to decode different types of files, tracks or streams the user can play.
Output plug-ins is the final destination of the decoded data. It can be rerouted to a sound card, to the hard disk or anywhere else appropriate. The data is in standard PCM format.
Filter plug-ins are chained between the decoder and the output plug-in to modify the PCM data before getting to the output plug-in.
See also common plug-in data types.
plugin.h contains the necessary structures
for all PM123 plug-ins.
All exported and callback functions must use the calling convention _cdecl
.
Before you include any file from the PDK you should define the macro PLUGIN_INTERFACE_LEVEL to the appropriate value matching the interface compatibility of your plugin. Example:
#define PLUGIN_INTERFACE_LEVEL 3
#include <plugin.h>
#include <decoder_plug.h>
A plug-in must have a function that identifies it as a plug-in:
plugin_query is called before any other function and exactly once by PM123. The plug-in will then have to fill the variables in the param structure, for example:int DLLENTRY plugin_query(PPLUGIN_QUERYPARAM param);
param->type = PLUGIN_VISUAL;
/* Identify the plug-in as visual plug-in. Types can be ORred to
include multiple plug-in types in the same DLL. */
param->author = "Matti Meikäläinen";
/* Author of the plug-in */
param->desc = "Example plug-in";
/* A short description of the plug-in */
param->configurable = TRUE;
/* Toggles plug-in configurability via PM123 Properties dialog */
param->interface = PLUGIN_INTERFACE_LEVEL;
/* This is the required interface revision level.
* This parameter defaults to 0 which is the same as before this field existed.
* Plug-ins with Level 0 must not access this field to be compatible with older
* versions of PM123. */
return 0;
A second initialization function, plugin_init, is called by the plug-in manager once per plug-in after plugin_query. The function is called only from the application's main thread. It should not block for longer nor do I/O operations to keep the GUI responsive.
int DLLENTRY plugin_init(const PLUGIN_CONTEXT* ctx);
The export of this function is optional but level 2 and up plug-ins most likely will require it. It provides some global entry points of PM123. They might be used to handle asynchronous events or requests. The structure and the functions must not be used after plugin_deninit returned.
/** message function */
void DLLENTRY (*message_display)(MESSAGE_TYPE type, const char* msg);
/** retrieve configuration setting */
int DLLENTRY (*profile_query)(const char* key, void* data, int maxlength);
/** store configuration setting */
int DLLENTRY (*profile_write)(const char* key, const void* buffer, int length);
/** execute remote command
* See the documentation of remote commands for a description. */
const char* DLLENTRY (*exec_command)(const char* cmd);
/** Invalidate object properties
* @param what A bit-vector of INFOTYTE
* @return Bits of what that caused an invalidation. */
int DLLENTRYP(obj_invalidate)(const char* url, int what);
/** Check whether a decoder claims to support this kind of URL and type.
* @param url URL of the object to check
* @param type .type extended attribute or mime type respectively.
* Multiple types may be tab separated.
* @return != 0 -> yes
* @remarks The function does not actually cause any I/O.
* It is not reliable during plug-in initialization. */
int DLLENTRYP(obj_supported)(const char* url, const char* type);/* Allocate dynamic string. Any previous content is discarded first.
* The returned memory can be written up to len bytes until the next
* xstring_* function call on dst. The return value is the same than
* dst->cstr except for constness. */
char* DLLENTRY (*xstring_alloc)(xstring* dst, unsigned int len);
/* Deallocate dynamic string. This will change the pointer to NULL. */
void DLLENTRY (*xstring_free)(volatile xstring* dst);
/* Return the length of a dynamic string */
unsigned DLLENTRY (*xstring_length)(const xstring* src);
/* Compare two xstrings for (binary!) equality. NULL allowed. */
int DLLENTRY (*xstring_equal)(const xstring* src1, const xstring* src2);
/* Reassing dynamic string from C string. Any previous content is discarded first. */
void DLLENTRY (*xstring_assign)(volatile xstring* dst, const char* cstr);
/* Copy dynamic string to another one. Any previous content is discarded first.
* This function will not copy the string itself. It only creates an additional reference to the content. */
void DLLENTRY (*xstring_copy)(volatile xstring* dst, const xstring* src);
/* Strongly thread safe version of xstring_copy. */
void DLLENTRY (*xstring_copy_safe)(volatile xstring* dst, volatile const xstring* src);
/* Append to xstring. The source may also be from a xstring.
* If dst is NULL a new string is created */
void DLLENTRY (*xstring_append)(xstring* dst, const char* cstr);
/* printf into a xstring. Any previous content is discarded first. */
void DLLENTRY (*xstring_sprintf)(volatile xstring* dst, const char* fmt, ...);
void DLLENTRY (*xstring_vsprintf)(volatile xstring* dst, const char* fmt, va_list va);
The exec_command
function causes
PM123 to execute a remote command as if it were sent to the remote pipe
interface. It returns the reply string. The returned storage is valid
until the next call to exec_command or until plugin_deinit.
Calls to exec_command must be
serialized.
The remote state information
like the currently selected playlist is private to the plug-in and does
not interfere with commands sent to the remote pipe or from another
plug-in.
query_profile and write_profile
are similar to the profile OS/2 API functions PrfQueryProfileData
and PrfWriteProfileData.
But they read and write to a section dedicated to your plug-in the
current PM123.INI file which may not be the one in the application
folder. Using this functions is recommended over creating an individual
profile.
query_profile returns the length of the requested
Parameter or -1 on error. Independent of the returned length at most maxlen
characters are stored in buffer.
If you pass NULL as key,
a list of '\0' delimited keys is
returned. write_profile returns TRUE
on success and FALSE on error.
The xstring_* API functions are used to manipulate dynamic strings of type xstring.
If you set param->configurable = TRUE in plugin_query, a configuration dialog should appear when PM123 calls
HWND DLLENTRY plugin_configure(HWND owner, HMODULE module);
where owner is the notebook or player window so that you can "lock" your window on it if you want and where module can be used to load a resource from your DLL. The functions is called only from the application's main thread. It should not block for longer nor do I/O operations to keep the GUI responsive.
The function should return a window handle if and only if the configuration dialog in non-modal, i.e. it is still open when plugin_configure returns. PM123 might then call plugin_configure again with owner = NULLHANDLE to close the dialog. If the configuration dialog is modal you need not to care about that. NULLHANDLE should always be returned.
Note that the return value is new to interface level 3. PM123 uses this value to keep track of open configuration windows. Older PM123 versions ignore the return value.
The export of plugin_ocommand is optional. If the symbol is exported, the plug-in can receive remote commands.
void DLLENTRY plugin_command(const char* command, xstring* result);
The command parameter receives the command passed to the remote interface without the asterisk and plug-in name prefix. The handling is plug-in specific. result should be assigned with the result of the command if any. Note that these commands must not be used to control the primary function of a plug-in like to stop decoding a file. It is mainly intended for remote configuration.
Plug-ins should deinitialize and destroy their windows and free allocated memory when receiving a
int DLLENTRY plugin_deinit(int unload);
It can also be used to save settings in your INI file for other sort of plug-ins.
The interface level is used to ensure compatibility of
plug-ins over different versions of PM123. The field defaults to 0
representing the oldest implementation. Larger values reflect changes
to the plug-in interface. The interface level reflects changes to any
of the plugin interfaces, so different levels do not necessarily mean
different interfaces of one plug-in interface. A
change in the interface may be only a modified semantic of a function
call or it may be a complete change of the interface with entirely
other function names or whatever. See the individual PDK documentation
of the desired plug-in type to get further information for each plug-in
type.
A new plug-in is not necessarily required to use the most recent
interface level.
Overview:
Level | PM123 version | Visual | Decoder | Output | Filter |
---|---|---|---|---|---|
0 | n/a 1 | no longer supported! | supported | supported, deprecated | supported, deprecated |
1 | ≥ 1.32 1 | no longer supported! | supported, same as level 0 |
supported but deprecated, same as level 0 |
supported, deprecated, same as level 0 |
2 | = 1.40 2 | no longer supported! same as level 1 |
no longer supported! | no longer supported! | no longer supported! |
3 | = 1.41 | recent | recent | recent | recent |
For example a filter plug-in will be loaded depending on it's interface level by a PM123 instance with interface level 3 (compile time constant) in the following way:
Interface level of the plug-in |
Action taken |
---|---|
3 | Loaded native. |
2 | Error, because level 2 is discontinued. |
1 | Loaded via a proxy in compatibility mode. |
0 | Loaded via a proxy in compatibility mode. Level 0 is identical to level 1 with respect to the filter plug-in interface. |
>3 | Error, because the interface is potentially incompatible and not supported by this PM123 core. |