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
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.
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 )
OUTPUT_OPEN
opens the device or file needed for output.OUTPUT_CLOSE
immediately stop playback and close the audio device.
OUTPUT_VOLUME
changes the volume of an output device.
OUTPUT_PAUSE
pauses the playback (ie.: block in output_request_buffer()
).
OUTPUT_SETUP
setup the format that output_request_buffer()
will most likely receive,
error_display functions and hwnd.
OUTPUT_TRASH_BUFFERS
trash any buffers currently awating to be played.
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:
OUTPUT_VOLUME
OUTPUT_SETUP
OUTPUT_OPEN
- now we are active.
The data interface may be used.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.
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.
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 )
NULL
indicates that there are no more samples to come (flush signal).
sizeof(float)
.
Return values ≤ 0 signals a fatal error, except for the flush signal which always returns zero.
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 )
output_request_buffer
.
This does not mean that it is a good advise to play a buffer smaller than usual unless you receive a flush signal.
A length of 0 is not an error but an undo request to the previous output_request_buffer
call.
output_playing_pos
.
This is a time index of the starting point of this buffer in milliseconds.
The time position marker may have a large offset far beyond the length of the current file.
This function is called by the decoder or last in chain filter plug-in to play the stored samples.
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.
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).
See level 2 interface. This part of the interface has not been changed.
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 )
OUTPUT_OPEN
opens the device or file needed for output.
OUTPUT_CLOSE
closes it.
OUTPUT_VOLUME
changes the volume of an output device.
OUTPUT_PAUSE
pauses the playback (ie.: block in output_playsamples()).
OUTPUT_SETUP
setup the format that output_playsamples() will most likely
receive, boost priority values, error_display functions and hwnd.
OUTPUT_TRASH_BUFFERS
trash any buffers currently awating to be played.
OUTPUT_NOBUFFERMODE
forces the plug-in to not accumulate buffers for the time being.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:
OUTPUT_VOLUME
OUTPUT_SETUP
OUTPUT_OPEN
- now we are active.
The data interface may be used.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:
WM_PLAYERROR
when a playback error occures.
WM_OUTPUT_OUTOFDATA
when the output plug-in has finished playing all.
its buffers. Not needed when always_hungry flag is enabled.
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 )
WAVE_FORMAT_PCM
and from PM123 version 1.40 the number of bits per sample is always 16.
The number of channels may change during playback but not within a buffer.
output_playing_pos
.
This function is called by the decoder or last in chain filter plug-in to play samples.
See level 2 interface. This part of the interface has not been changed.