Output plugins must implement and export the functions defined in output_plug.h. There are different interface versions. It is recommended to use level 3.
The initialization interface does not need to be thread safe.
ULONG DLLENTRY output_init (struct OUTPUT_STRUCT** a)
ULONG DLLENTRY output_uninit(struct OUTPUT_STRUCT* a)
The output_init function is called when the user requests the
use of your output plugin. So only one output plugin is active at any
given time. It should initialize the control variables for an eventual
call to output_command with OUTPUT_OPEN.
output_uninit is called when another output plugin 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(struct OUTPUT_STRUCT* a, ULONG msg, OUTPUT_PARAMS2* info)
OUTPUT_SETUP
OUTPUT_OPEN
- now we are active. The data
interface may be used.OUTPUT_CLOSE
- now we are no longer active.The other function codes (OUTPUT_VOLUME
, OUTPUT_PAUSE
and OUTPUT_TRASH_BUFFERS
) are allowed only in active mode.
Only the fields OutEvent and W should be read during this call.
This call tells the device about the object that is about to be played (fields URL and Info). The Output could use this information to get the format (sampling rate and channels) that is likely to come. The field PlayingPos contains the time index of the first sample of the decoded object.
The URL 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, if the playback moves to the next
file in playlist mode. This is signaled with a new OUTPUT_OPEN
call while the device is already open. The call is always before the first
decoded samples of the mentioned object arrive. The new object's data
start with the time index PlayingPos. Note that multiple short
items might be in the processing queue before their samples arrive. But
usually only one song is decoded in advance.
As long as you do not care about the new object you could ignore this
message. But note that the format might change with a new item.
This command tells the output to discard any buffers not yet played. It is used to make seek operations responsive. This implies also to discard an outstanding flush request and any information from OUTPUT_OPEN calls, except for the last one.
This message should immediately release a thread waiting at output_request_buffer for data to be played. This is essential to stop playback while in paused mode.
If the target is a sound device, the output plugin should usually
immediately respond with an OUTEVENT_LOW_WATER event, because
there are no more buffers to play at the moment.
On this call the Volume field contains the new volume setting. The change should apply as soon as possible.
The values are in the range [0,1]. The scale should behave like a volume control of a hi-fi unit, i.e. it should not use a linear scale. If your hardware or API has a linear scale, you could use the approximation linscale = value / (1 + sqrt(10) * (1 - value)).
PM123 since 1.41 will no longer call this function before OUTPUT_OPEN.
The command should immediately pause or resume playback, depending on the Pause field in the control structure. If playback is paused the plugin should sooner or later block in output_request_buffer if the buffers become filled.
There are two cases, when OUTPUT_CLOSE can arrive.
output_event
with the parameter OUTEVENT_PLAY_ERROR
when a
playback error occurs. This will stop the playback, but it is not
guaranteed that this is done immediately.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).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 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 3 interface allocates the sample buffers by the plugin 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 alternately.
Anything else is an error.
All samples are passed as 32 floating point regardless what type the input has been.
int DLLENTRY output_request_buffer(struct OUTPUT_STRUCT* 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 plugin to store samples for playing. The function call may block until enough buffer space is available.
After a flush request (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 shall not block until
all buffers are played.
Do not close the output if you receive a flush request. The user could use
navigation commands to resume playback while the flush request already is
in the processing queue.
void DLLENTRY output_commit_buffer(struct OUTPUT_STRUCT* a, int len, PM123_TIME pos)
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 seconds.
The time position marker may have a large offset far beyond the length
of the current file. This functions are called by the decoder or last in chain filter plugin to play the stored samples.
The status interface must be thread safe and re-entrant.
This function returns the pos from the buffer that the user currently hears. The return value is a time index in seconds. The plugin may use its knowledge to calculate the time with higher resolution than one buffer. But you must not make any assumptions about the zero point.PM123_TIME DLLENTRY output_playing_pos(struct OUTPUT_STRUCT* a)
Returns TRUE if the output plugin still has some buffers to play.BOOL DLLENTRY output_playing_data(void* a)
ULONG DLLENTRY output_playing_samples(struct OUTPUT_STRUCT* a, PM123_TIME offset, OUTPUT_PLAYING_BUFFER_CB cb, void* param)
typedef void (DLLENTRY* OUTPUT_PLAYING_BUFFER_CB)(void* param, const FORMAT_INFO2* format, const float* samples, int count,
PM123_TIME pos, BOOL* done);
This callback receives chunks of sample data that is currently played by the output plugin.
This interface revisions should not be used for development because the
implementations 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 3 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(struct OUTPUT_STRUCT *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 plugin 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_OPEN
- next songOUTPUT_CLOSE
- now we are no longer active.The other function codes (OUTPUT_PAUSE
and OUTPUT_TRASH_BUFFERS
)
are allowed only in active mode. OUTPUT_VOLUME
is always
allowed.
The output plugin must WinPostMsg() the following messages to hwnd:
WM_PLAYERROR
when a playback error occures. WM_OUTPUT_OUTOFDATA
when the output plugin 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(struct OUTPUT_STRUCT* 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 plugin to play samples.
The status interface must be thread safe and reentrant.
This function returns the posmarker from the buffer that the user currently hears. The return value is a time index in milliseconds. The plugin may use this knowledge to calculate time indices with higher resolution than one buffer. But you must not make any assumptions about the zero point.int DLLENTRY output_playing_pos(struct OUTPUT_STRUCT* a)
Returns TRUE if the output plugin still has some buffers to play.BOOL DLLENTRY output_playing_data(struct OUTPUT_STRUCT* a)
ULONG DLLENTRY output_playing_samples(struct OUTPUT_STRUCT* a, FORMAT_INFO* info, char* buf, int len)
This function is used by visual plugins 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 plugins before making complicated buffering functions in your output plugin.