I learned a lot from trial-and-error and debug inspection about how Winamp interacts with the encoder plugins (enc_*.dll) while working on an Opus encoder plugin. The same problems I had interacting with WASABI objects I also had with AudioCoder objects, in that they also are MSVC classes. I thought it would be simple enough to define a new encoder object handler like I did for WASABI classes, which was close, but not all the way.
[SMALL_RANT]
When Winamp is done with an encoder object it deletes it. Now for the layman this might not seem nefarious on it's own but to a third-party library developer it's actually not good (some would even say "bad form".) Deleting a class you did not create is dangerous, even when deleting a class from a DLL compiled with the same compiler, regardless if the destructor method is virtual. And since encoder plugins can (theoretically) be developed by any third-party developer, then submitted and reviewed there's no guarantee that it will be compiled with the same compiler.
[/SMALL_RANT]
Long story short, I had to add a special handling mechanism into my handling of my Winamp AudioCoder class, specifically, adding a method to the fake v-table pointer for handling calls to the AudioCoder's destructor.
Notes for new encoder developers (these are just some notes from off the top of my head that I had to discover the hard way):
I am attaching my encoder handler source unit for Borland compilers. Enjoy.
[SMALL_RANT]
When Winamp is done with an encoder object it deletes it. Now for the layman this might not seem nefarious on it's own but to a third-party library developer it's actually not good (some would even say "bad form".) Deleting a class you did not create is dangerous, even when deleting a class from a DLL compiled with the same compiler, regardless if the destructor method is virtual. And since encoder plugins can (theoretically) be developed by any third-party developer, then submitted and reviewed there's no guarantee that it will be compiled with the same compiler.
[/SMALL_RANT]
Long story short, I had to add a special handling mechanism into my handling of my Winamp AudioCoder class, specifically, adding a method to the fake v-table pointer for handling calls to the AudioCoder's destructor.
Notes for new encoder developers (these are just some notes from off the top of my head that I had to discover the hard way):
- When you have exported a "PrepareToFinish" or "PrepareToFinishW" function (which is apparently optional), this is a signal to your plugin that there is no more audio data left and to flush all remaining file data out on the next call to "Encode". "Encode" will continue to be called until you return 0 so don't worry if you have more data than the out buffer can hold at the time of the call. The file that is being written to still has a handle open on it so I suggest waiting until "FinishAudio3"/"FinishAudio3W" is called to finalize the file (if needed.)
- When "FinishAudio3" or "FinishAudio3W" is called by Winamp, all file handles (at least by Winamp) have been closed and it's (theoretically) safe to modify it for finalization by whatever your library is encoding. This can include (but is not limited to) adding tags/headers/footers, parameter tweaks, or whatever the new file might need for actual use/playback.
- The functions "CreateAudio3", "ConfigAudio3", "SetConfigItem", and "GetConfigItem" all have a parameter labeled "configfile". Use of that data is NOT optional. Normally it points to the ML Transcoder configuration file although it can point to some other file where another developer can specify a different location for other purposes. If your encoder has parameters that are user configurable from your configuration window (the one that is made when Winamp calls "ConfigAudio3"), you NEED to store those settings in the file pointed to by "configfile". The main reason for this is in the transition between when your config window is displayed and when "CreateAudio3" is called, your plugin IS COMPLETELY UNLOADED, which means all the configuration parameters the user just set went bye-bye. The only way to get them back is through that config file. Since the file pointed to by "configfile" is usually the same as the other encoder plugins (and the Transcoder ML plugin), you will need to play nice and define a unique INI section for your plugin (and keep the configuration parameters only in your section).
- For Winamp, the PCM type is defined as 0x204D4350 or "PCM ". Example: "const unsigned int pcmtype='PCM ';//0x204D4350"
- Before "CreateAudio3" is called, "GetConfigItem" is called with the "item" parameter set to "extension". When this happens, it would be good to return the actual file extension used for your encoder without the delimiting extension dot, but double check to make sure that the length of the "data" parameter (as indicated by the "len" parameter) is large enough to accommodate your actual file extension. At the moment I believe it's 5 so if your file extension is longer than "len", output what you can (taking into consideration the NULL terminator), then rename the file when "FinishAudio3"/"FinishAudio3W" is called.
- The dimensions of your configuration window: width<=384; height<=271. This window will be a child window to the window handle given by the "hwndParent" parameter in the call to "ConfigAudio3". This is not the same as Winamp's main window.
I am attaching my encoder handler source unit for Borland compilers. Enjoy.
Comment