PM123 Output Plug-ins

Output plug-ins must implement and export the functions defined in output_plug.h. There are two interface versions. It is strongly recommended to use (at least) level 2.

Before including any PM123 include file the macro OUTPUT_PLUGIN_LEVEL must be defined if you want to use anything higher than level 0. Example:

     #define OUTPUT_PLUGIN_LEVEL 2

Interface revision level 2 (recommended)

Initialization interface

The initialization interface does not need to be thread safe.

     ULONG DLLENTRY output_init  ( void **a )
     ULONG DLLENTRY output_uninit( void  *a )

Init function is called when the user requests the use of your output plug-in. So only one output plug-in is active at any given time. It should initialize the control variables for an eventual call to OUTPUT_OPEN. decoder_uninit is called when another output plug-in is request by the user and should free the allocated memory for a.

Control interface

The control interface does not need to be thread safe itself, but it is called from a different thread than the data interface.

ULONG DLLENTRY output_command( void *a, ULONG msg, OUTPUT_PARAMS2 *info )

There is a lot of commands to implement for this function. Parameters needed for each of them are described in the definition of the structure in the .h file.

The status graph is as follows:

  1. OUTPUT_VOLUME
  2. OUTPUT_SETUP
  3. OUTPUT_OPEN - now we are active. The data interface may be used.
    Optionally repeat this step.
  4. OUTPUT_CLOSE - now we are no longer active.

The funtion codes (OUTPUT_PAUSE and OUTPUT_TRASH_BUFFERS) are allowed only in active mode. OUTPUT_VOLUME is always allowed.

Notes

The output plug-in MUST call the eventhandler output_event with the parameter OUTEVENT_PLAY_ERROR when a playback error occures. This will stop the playback, but it is not guaranteed that this ist done immediately.
The output plug-in must call output_event with OUTEVENT_END_OF_DATA when the last sample is really processed. This must not be done unless the function output_play_samples has been called with a buffer pointer of NULL (flush signal).
The output plug-in may raise the events OUTEVENT_LOW_WATER and OUTEVENT_HIGH_WATER to signal that the buffers are running low or the buffers are getting full respectively. This is used by PM123 to control the scheduling priority.
The event handler may be called from any thread in any context.

The URI parameter at the OUTPUT_OPEN call uses the following syntax:
file:///D:/Music/my favorite song.mp3
file://server/share/favorite song.mp3 (UNC path)
http://... (as you would expect)
cdda:///H:/track02
The URI may change during playback. This is signalled with a new OUTPUT_OPEN call. You may ignore this.

Data interface

The data interface is only used when the control interface is in activated state.

The data interface does not need to be thread safe itself, but it is called from a different thread than the control interface.

The level 2 interface allocates the sample buffers by the plug-in rather than the data source. This is known to cause less double buffering. The buffer size is no longer fixed. The functions output_request_buffer and output_commit_buffer have to be called alternatingly. Anything else is an error.

All samples are passed as 16 bit signed integers regardless what type the input has been.

     int DLLENTRY output_request_buffer( void* a, FORMAT_INFO2* format, float** buf )

This function is called by the decoder or last in chain filter plug-in to store samples for playing. The function call may block until enough buffer space is available.

After a flush signal (buf == NULL) there is no need to call output_commit_buffer. The flush signal should be used to play any internal buffers regardless if they are completely full or not.
Note that the flush signal is an implicit request for an OUTEVENT_END_OF_DATA event. The output_request_buffer call must not block until all buffers are played.

     void DLLENTRY output_commit_buffer( void* a, int len, int posmarker )

This function is called by the decoder or last in chain filter plug-in to play the stored samples.

Status interface

The status interface must be thread safe and reentrant.

     int DLLENTRY output_playing_pos( void* a )
This function returns the posmarker from the buffer that the user currently hears. The return value is a time index in milliseconds. The plug-in may use this knowledge to calculate time indices with higher resolution than one buffer. But you must not make any asumptions about the zero point.
     BOOL  DLLENTRY output_playing_data( void* a )
Returns TRUE if the output plug-in still has some buffers to play.
     ULONG DLLENTRY output_playing_samples( void* a, FORMAT_INFO* info, char* buf, int len )

This function is used by visual plug-ins so the user can visualize what is currently being played. len is usually in the order of 2048 samples or less. So check that amount usually required by your visual plug-ins before making complicated buffering functions in your output plug-in.

Interface revision level 0 and 1 (deprecated)

This interface revisions should not be used for development because the impementations tends to modify the current thread's priority to boost the speed of the data source. This implies that the current thread while calling output_play_samples is the bottleneck and that it is always the same thread. Both is not true in general (e.g. remote data sources).

Initialization interface

See level 2 interface. This part of the interface has not been changed.

Control interface

The control interface does not need to be thread safe itself, but it is called from a different thread than the data interface.

ULONG DLLENTRY output_command( void *a, ULONG msg, OUTPUT_PARAMS *info )

There is a lot of commands to implement for this function. Parameters needed for each of them are described in the definition of the structure in the .h file.

The status graph is as follows:

  1. OUTPUT_VOLUME
  2. OUTPUT_SETUP
  3. OUTPUT_OPEN - now we are active. The data interface may be used.
  4. OUTPUT_CLOSE - now we are no longer active.

The other funtion codes (OUTPUT_PAUSE and OUTPUT_TRASH_BUFFERS) are allowed only in active mode. OUTPUT_VOLUME is always allowed.

The output plug-in MUST WinPostMsg() the following messages to hwnd:

Data interface

The data interface is only used when the control interface is in activated state.

The data interface does not need to be thread safe itself, but it is called from a different thread than the control interface.

     int DLLENTRY output_play_samples( void* a, FORMAT_INFO* format,
                                          char* buf,int len, int posmarker )

This function is called by the decoder or last in chain filter plug-in to play samples.

Status interface

See level 2 interface. This part of the interface has not been changed.