mxPlay Programmer's Plugin API Reference ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1. Introduction 2. Structures 2.1 Header 2.1.1 MXP_PLUGIN_ID 2.1.2 MXP_PLUGIN_PARAMETER 2.1.3 MXP_PLUGIN_REGISTER_MOD 2.1.4 MXP_PLUGIN_PLAYTIME 2.1.5 MXP_PLUGIN_INIT 2.1.6 MXP_PLUGIN_SET 2.1.7 MXP_PLUGIN_UNSET 2.1.8 MXP_PLUGIN_DEINIT 2.1.9 MXP_PLUGIN_MUSIC_FWD 2.1.10 MXP_PLUGIN_MUSIC_RWD 2.1.11 MXP_PLUGIN_MUSIC_PAUSE 2.1.12 MXP_PLUGIN_INFO 2.1.13 MXP_PLUGIN_EXTENSIONS 2.1.14 MXP_PLUGIN_SETTINGS 2.2 Info Structure 2.2.1 mxp_struct_info_plugin_author 2.2.2 mxp_struct_info_plugin_version 2.2.3 mxp_struct_info_replay_name 2.2.4 mxp_struct_info_replay_author 2.2.5 mxp_struct_info_replay_version 2.2.6 mxp_struct_info_flags 2.3 Settings Structure 2.3.1 mxp_struct_settings_name 2.3.2 mxp_struct_settings_type 2.3.3 mxp_struct_settings_routine_set 2.3.4 mxp_struct_settings_routine_get 2.4 Extensions Structure 2.4.1 mxp_struct_extensions_string 2.4.2 mxp_struct_extensions_name 3. Functions 3.1 Register() 3.2 Playtime() 3.3 Init() 3.4 Set() 3.5 Unset() 3.6 Forward() 3.7 Rewind() 3.8 Pause() 4. Remarks 4.1 Startup Process 4.2 Communication Issues 4.3 Stability 4.4 What You Don't Need To Do 5. Contact ---------------------------------------------------------------------- ******************* * 1. Introduction * ******************* Writing any plugin-based application is always very tricky. You have to provide some universal, in the future expandable, easy to understand and, at last but not least, working interface between 'server' application and 'client' plugin. My goal was to remove as much work from programmer as possible. I think this approach is not only good for programmer but it increases overall stability since application has control over more things. All mentioned constants and structures are defined in the file MXP_INC.S. ***************** * 2. Structures * ***************** Each plugin should be compiled as normal .PRG binary, for example from Devpac. 2.1 Header ========== Offsets are relative to the begin of text segment. So it's very useful to create some label at the begin of your code and to use predefined offets. For example: ogg_header: dc.l "MXP1" ds.l 1 bra.w register_module : : and then access to the members using: lea ogg_header,a0 move.l d0,(a0,MXP_PLUGIN_PARAMETER) or: move.l d0,ogg_header+MXP_PLUGIN_PARAMETER Please note all entries are 4 bytes long. 2.1.1 MXP_PLUGIN_ID ------------------- Characters 'MXP1', i.e. ASCII identifier. 2.1.2 MXP_PLUGIN_PARAMETER -------------------------- Storage place for both application and plugin. This is the only place where they are allowed to write. For more about this, see chapter 4.2 'Communication Issues'. The content can be either constant or pointer, depending on the passed data, see discussion about structures bellow. I didn't want to use register-based communication since every C compiler passes parameters on stack differently and I wanted to do portable code. 2.1.3 MXP_PLUGIN_REGISTER_MOD ----------------------------- Relative jump to Register() function. 2.1.4 MXP_PLUGIN_PLAYTIME ------------------------- Relative jump to Playtime() function. 2.1.5 MXP_PLUGIN_INIT --------------------- Relative jump to Init() function. 2.1.6 MXP_PLUGIN_SET -------------------- Relative jump to Set() function. 2.1.7 MXP_PLUGIN_UNSET ---------------------- Relative jump to Unset() function. 2.1.8 MXP_PLUGIN_DEINIT ----------------------- Relative jump to Deinit() function. 2.1.9 MXP_PLUGIN_MUSIC_FWD --------------------------- Relative jump to Forward() function. 2.1.10 MXP_PLUGIN_MUSIC_RWD --------------------------- Relative jump to Rewind() function. 2.1.11 MXP_PLUGIN_MUSIC_PAUSE ----------------------------- Relative jump to Pause() function. 2.1.12 MXP_PLUGIN_INFO ---------------------- Pointer to the 'Info' structure. 2.1.13 MXP_PLUGIN_EXTENSIONS ---------------------------- Pointer to the list of 'Extensions' structures. 2.1.14 MXP_PLUGIN_SETTINGS -------------------------- Pointer to the list of 'Settings' structures. 2.2 Info Structure ================== Defined as 'mxp_struct_info'. Each entry is 4 bytes long. The number of structures is limited to one. Best approach is to use it for example as: lea ogg_header,a0 movea.l (a0,MXP_PLUGIN_INFO),a0 move.l (a0,mxp_struct_info_plugin_author),d0 : : 2.2.1 mxp_struct_info_plugin_author ----------------------------------- Pointer to NULL terminated string with plugin author's name. 2.2.2 mxp_struct_info_plugin_version ------------------------------------ Pointer to NULL terminated string with plugin version. 2.2.3 mxp_struct_info_replay_name --------------------------------- Pointer to NULL terminated string with replay routine's name. 2.2.4 mxp_struct_info_replay_author ----------------------------------- Pointer to NULL terminated string with replay routine's author name. 2.2.5 mxp_struct_info_replay_version ------------------------------------ Pointer to NULL terminated string with replay routine's version. 2.2.6 mxp_struct_info_flags --------------------------- This bitfield tells plugin which resources wish to use. Currently defined are: - MXP_FLG_USE_DSP: plugin uses DSP - MXP_FLG_USE_DMA: plugin uses DMA sound system - MXP_FLG_USE_020: plugin uses 020+ CPU - MXP_FLG_USE_FPU: plugin uses FPU So you use for example: dc.l MXP_FLG_USE_DSP|MXP_FLG_USE_DMA|MXP_FLG_USE_020 2.3 Settings Structure ====================== Defined as 'mxp_struct_settings'. Each entry is 4 bytes long. The number of structures is unlimited. 2.3.1 mxp_struct_settings_name ------------------------------ Pointer to NULL terminated string with parameter's name. If NULL, no more parameters are read. This name is used as unique identifier so you can't use two parameters with the same name. 2.3.2 mxp_struct_settings_type ------------------------------ Type of parameter. Currently defined are: - MXP_PAR_TYPE_BOOL: handle as 1 / 0 switch. - MXP_PAR_TYPE_CHAR: handle as string parameter - MXP_PAR_TYPE_INT: handle as signed integer parameter These types tell player what value will get after calling 'get' function or to what type should convert value which is on input of 'set' function. Please, don't use very long names for these parametes, you wont break anything, but you can get to the situation when you will have 'very_long_parameter_name1' and 'very_long_parameter_name2' and application will see one 'very_long_parame' parameter). There's a little different behaviour by interpreting values of these parameters according to the group they belong to. For plugin parametes applies one general rule: output size is limited by the size of Plugin Info dialog. It's simply because there's a very small chance you will ever need more than a few charactes (in fact I can't imagine what kind of parameter you could pass to the plugin as CHAR type). Except this, there's a different interpretation of the BOOL parameter (comparing to module parameters): it's shown as checkable custom defined colour icon. So state '1' means icon checked and vice versa. However, module parameters give the user possibility to scroll these values. So your parameter value (e.g. songname) can contain unlimited number of characters. Around this value will be shown left/right arrows which allow user to view te rest of text. Please note EVERY parameter will be converted to the string - both BOOL type (as 'Yes' / 'No') and integer. Each parameter type could/should be ORed with one or more flags: - MXP_FLG_INFOLINE: parameter + its value will be shown on panel in scrolling infoline - MXP_FLG_MOD_PARAM: parameter + its value will be shown in Module Info window - MXP_FLG_PLG_PARAM: parameter + its value will be shown in Plugin Info window For example: dc.l param_original dc.l MXP_PAR_TYPE_BOOL|MXP_FLG_MOD_PARAM : : param_original: dc.b 'Original',0 and when 'get' will return 0, in the Module Info window you will see 'Original: No'. 2.3.3 mxp_struct_settings_routine_set ------------------------------------- Pointer to the 'Set' function. As was mentioned, if NULL, parameter will be marked as 'read only' (will be shaded in info window). Of course, this has some sense only in plugin parameters. Please, don't mess up this function with the one discussed in chapter 3. Typical use of this function is to switch on/off some parameter (interpolation, surround, ...) or to pass some integer value (e.g. playtime if plugin doesn't provide accurate playtime determination). However, there's also text parameter for some special use. After calling this function from application, plugin should expect value at MXP_PLUGIN_PARAMETER place. Application doesn't check any return code. 2.3.4 mxp_struct_settings_routine_get ------------------------------------- Pointer to 'Get' function. This pointer can never be NULL! Plugin should return wanted value at MXP_PLUGIN_PARAMETER place. Application doesn't check any return code. 2.4 Extensions Structure ======================== Defined as 'mxp_struct_extensions'. Each entry is 4 bytes long. The number of structures is unlimited. 2.4.1 mxp_struct_extensions_string ---------------------------------- Pointer to NULL terminated string with supported module file extension. If NULL, no more parameters are read. 2.4.2 mxp_struct_extensions_name -------------------------------- Pointer to NULL terminated string with the module extension full name. For example: dc.l am_extension_string dc.l am_extension_name : : am_extension_string: dc.b "AM",0 am_extension_name: dc.b "ACE Module",0 **************** * 3. Functions * **************** This is the base interface between application and plugin. All functions except Playtime() should return result status in d0 register - MXP_OK or MXP_ERROR. 3.1 Register() ============== Register module. On the input (in the input buffer) plugin should expect pointer to two another pointers: - buffer with module binary - length of this buffer Correct handling of this input is as follows: movea.l ogg_header+MXP_PLUGIN_PARAMETER,a0 move.l (a0)+,ogg_buffer move.l (a0),ogg_buffer_length Please note you have to fill all things in Settings structure *NOW* ! Return MXP_OK if plugin is able to replay given module. Return MXP_ERROR if not. This could occur when you compare file header to some expected value and the values don't match. 3.2 Playtime() ============== Return playtime for given module - some plugins are able to calculate it. If your plugin isn't able to do it, return let's say 2 minutes and allow user to change this value. 3.3 Init() ========== Initialize plugin. Allocate space, precalc tables etc. Don't do anything with HW registers! 3.4 Set() ========= Set module, i.e. save HW registers, set new ones, activate playing. 3.5 Unset() =========== Opaque to Set(), i.e. stop playing, restore HW registers. 3.6 Forward() ============= Skip some time forward. Currently unimplemented. 3.7 Rewind() ============ Move back some time. Currently unimplemented. 3.8 Pause() =========== Pause playing. You have to care about on/off state, application will just call this function. Don't forget to deactivate pause status when Unset() is called. ************** * 4. Remarks * ************** Some important remarks about plugin making. 4.1 Startup Process =================== 1. Load plugin, call Init(). 2. When user loads module, call Register(). [after this step infoline will be filled via Get() functions in Settings structure] 3. If Register() was OK, call Set(); if not, release module from memory (!) 4. When user stops playing, call Unset(). 5. When user presses pause key, call Pause(). 6. [Currently unimplemented] When user request to remove plugin, call Deinit(). 4.2 Communication Issues ======================== The application and plugin behave as classical client/server model. Each plugin is handled as new process (using Pexec() call). That means they are independent processes which theoretically can't share any memory. There are some solutions how to do it, I chose probably the simplest one - they share some global memory. This applies to two buffers: - module buffer, which has to be accessible by both plugin and application - communication buffer, where plugin and application exchange data Since plugin has no reason to access any of application's data, there's no problem to set memory model as 'Private' for the application. However, application needs some access to plugin's structures (parameters) so *YOU HAVE TO SET AT LEAST READABLE ACCESS TO THE PLUGIN*. This can by done by many tools, from CPX to Jinnee's info dialog. 4.3 Stability ============= In the ideal case when application is free of bugs there's still very important thing which applies to overall application stability: stability of plugins. Typical example, how to hang FreeMiNT kernel and/or XaAES is to use some custom exception vector, e.g. Timer A/B/C/D. If there is no way how to avoid using it, please (PLEASE!) use following approach: - always save/restore everything you use - try to call original exception vector after you finished your stuff (yes, there are some potential problems with the situation if some other application uses similar approach and then such application restore original exception pointer... this is trying to solve XBRA protocol but... you know ;) - set memory model to 'Super'. This means except your plugin you provide access to your application space also for all applications in Supervisor mode (i.e. FreeMiNT kernel). This fix well know FATAL ERRORs in FreeMiNT with memory protection. I have to say, it's not ideal solution but there's no other way. MiNT guys are very upset when comes a talk to similar practices but do we want some good player or not? 4.4 What You Don't Need To Do ============================= Application try to do some work for you, concretly: - lock/unlock resources you've specified in the info structure. So you don't need to do it by hand. Or... you MUST NOT to do it ;) - convert bools and ints to strings and vice versa. So if you want to show your plugin replays 8 channel module you don't need to convert the number '8' to the string. - Mshrink(). Avoid using of this call, this is called on the startup time. - Super() calls. You don't need to call this call anytime. Every function as called via Supexec(). Yeah yeah, I know, not very safe but I have the control over super/user stack which I found as the most important factor. ************** * 5. Contact * ************** Since I'm the author of this API it comes to me as very easy and understable. However, don't hesistate to contact me with ANY your question, I'm open to answer even the most primitive one you can imagine ;) The main goal is to motivate people to code as much plugins as possible. So don't wait, take your favorite replay routine and make new MXP plugin! You can reach me 7 days of week at: mikro@hysteria.sk In case of some unexpected things (server crash etc) you can contact Xi, the author of the most of plugins in this release: xi@napri.sk ... happy plugging! ----------- Don't forget to visit ----------- ----------- http://mxplay.atari.org ----------- MiKRO / Mystic Bytes -XI- / Satantronic http://mikro.atari.org http://satantronic.atari.org http://msb.neostrada.pl ----------- Don't forget to visit ----------- ----------- http://atari.sk -----------