Developing PM123 Plug-ins

PM123 supports four kinds of plug-ins: visual, decoder, output and filter. 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.

Visual plug-in interface has not been changed since version 1.0, eventhough they could be ameliorated in the future. Currently, Visual plug-ins allow the creation of internal or external windows and they can tap into PM123 in several ways: they can retrieve currently playing samples, control PM123 and so on. Plug-ins are Dynamic Linked Libraries, DLLs, which PM123 loads on use.

Note that visual plug-ins cannot be loaded via PM123's Properties dialog because they are skin specific. They can of course be loaded when loading a new skin.

Common interface

plugin.h contains the necessary structures for all pm123 plug-ins. All exported and callback functions must use the calling convention _cdecl.

Initialization

A plug-in must have a function that identifies it as a plug-in:

int DLLENTRY plugin_query( PPLUGIN_QUERYPARAM param );
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:
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 = 2;
/* 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 fill 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.

int DLLENTRY plugin_init(const PLUGIN_CONTEXT* ctx);

The export of this function is optional. 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.

/* Error message function. */
void DLLENTRY (*error_display)( const char* msg );

/* Info message function */
/* This information is always displayed to the user right away. */
void DLLENTRY (*info_display)( const char* msg );

/* Execute remote command */
/* See the documentation of remote commands for a description. */
const char* DLLENTRY (*exec_command)( const char* cmd );

/* retrieve configuration setting */
int DLLENTRY (*query_profile)( const char* key, void* buffer, int maxlength );

/* store configuration setting */
int DLLENTRY (*write_profile)( const char* key, const void* buffer, int length );

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 other plug-ins.

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.

Configuration

If you set param->configurable = TRUE in plugin_query, a configuration dialog should appear when PM123 calls

void DLLENTRY plugin_configure( HWND hwnd, HMODULE module );

where hwnd 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.

Unload

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.

Plugin types

Now, which type of plug-in to you want to program?

Interface levels

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 neccessarily 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 plugin is not necessarily required to use the most recent interface level.

Overview:
Interface-Level PM123 version Visual Filter Decoder Output
0 n/a no longer supported! supported supported supported but deprecated
1 ≥ 1.32 1 recent supported,
same as level 0
supported,
same as level 0
supported but deprecated,
same as level 0
2 ≥ 1.4 recent,
same as level 1
recent redesign recent redesign recent redesign

  1. PM123 version 1.32 does not check for the compatibility of plug-ins. When a wrong plug-in is used, the application will most likely crash.

For example a filter plug-in will be loaded depending on it's interface level by a PM123 instance with interface level 2 (compile time constant) in the following way:
Interface level Action taken
2 Loaded native
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.
>2 Error, because the interface is potentially incompatible and not supported by this PM123 core.