|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionSeveral years ago, I struggled with reliably reading the tags of audio files in diverse formats and versions. No existing library managed to cope with the variety of emerging MP3 successors in my vast collection. At last, I settled on using Winamp input plug-ins. The basic idea was to restrict myself to the most basic fields of audio tags, yet manage these reliably for all future tag formats. I wrote a C++ wrapper DLL based on CDex source that relayed calls from VB6 clients and never used anything else. On the occasion of converting a client application to .NET, I tried a pure C# solution... and failed miserably. Recently, my second attempt amid several access violations proved successful. Regardless of my initial aim, you can probably use the presented class to play/decode audio with Winamp plug-ins. It might also be of interest if you are absolutely clueless about doing Interop with C-style function pointers. Documentation, info and inspiration: Background (C++)Winamp input plug-ins provide a reference to a 38-member structure, __declspec(dllexport) In_Module* (*WINAMPGETINMODULE2)( void );
After some information fields at the beginning, the typedef struct
{
int version; // module type (IN_VER == 0x100)
char *description; // description of module
HWND hMainWindow; // Winamp's main window (filled in by Winamp)
HINSTANCE hDllInstance; // DLL instance (filled in by Winamp)
char *FileExtensions; // "mp3,mp2,mpg"
// ...
void (*Config)(HWND hwndParent); // configuration dialog
void (*About)(HWND hwndParent); // about dialog
void (*Init)(); // called at program init
void (*Quit)(); // called at program quit
// ...
Out_Module *outMod; // filled in by Winamp
}
In_Module;
Winamp loads the plug-in and calls Loading a plug-in using C# InteropIn order to load a plug-in from managed C#, we convert the above types into their respective managed counterparts. The unmanaged function pointers are replaced by delegate types with an appropriate signature, plus a [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Size = 152)]
internal class In_Module
{
public delegate void VoidIntPtrdelegate(IntPtr hwndParent);
public delegate void VoidVoiddelegate();
// ...
public int version;
public string description;
public IntPtr hMainWindow;
public IntPtr hDllInstance;
public string FileExtensions;
// ...
[MarshalAs(UnmanagedType.FunctionPtr)]
public VoidIntPtrdelegate Config;
[MarshalAs(UnmanagedType.FunctionPtr)]
public VoidIntPtrdelegate About;
[MarshalAs(UnmanagedType.FunctionPtr)]
public VoidVoiddelegate Init;
[MarshalAs(UnmanagedType.FunctionPtr)]
public VoidVoiddelegate Quit;
// ...
public IntPtr outMod;
}
As [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal delegate IntPtr GetInModulePtr();
Sanity omitted: IntPtr hDll = Win32.LoadLibrary("in_mp3.dll");
IntPtr pFunc = Win32.GetProcAddress(hDll, "WinampGetInModule2");
GetInModulePtr delFunc = (GetInModulePtr)
Marshal.GetDelegateForFunctionPointer(pFunc, typeof(GetInModulePtr));
IntPtr pModule = delFunc();
In_Module inModule =
(In_Module)Marshal.PtrToStructure(pModule, typeof(In_Module));
inModule.hMainWindow = this.hWnd;
inModule.hDllInstance = hDll;
Marshal.StructureToPtr(inModule, pModule, false);
inModule.Init();
inModule = (In_Module)Marshal.PtrToStructure(pModule, typeof(In_Module));
// show plug-in's configuration dialog
inModule.Config(this.hWnd);
Debug.Print(inModule.FileExtensions);
inModule.Quit();
Win32.FreeLibrary(hDll);
The repeated copying of data is plainly inelegant, but comes with negligible performance cost. Unicode (updated)Beginning with Winamp 5.10, Nullsoft began implementing Unicode capabilities. The Winamp SDK 5.32 Beta defines a Unicode ExtendedFileInfo detailsWinamp SDK defines functions dealing with tags -- called typedef int (*WINAMPGETEXTENDEDFILEINFO) (
char* filename,char* metadata,char* ret,int retlen);
typedef int (*WINAMPSETEXTENDEDFILEINFO) (
char* filename,char* metadata,char* value);
typedef int (*WINAMPWRITEEXTENDEDFILEINFO)();
Newer plug-ins, such as Winamp 5.10+, export Unicode versions of these functions. The metadata parameter (always ANSI) identifies a particular field of the info as string, i.e. "artist," "bitrate." Setting info caches the data in the plug-in. Finally, it must be written to the file. As an example, we'll set the title field using a previously loaded plug-in: [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
private delegate int WinampSetExtendedFileInfo(
string filename, string metadata, string value);
IntPtr pFunc = Win32.GetProcAddress(hDll, "WinampSetExtendedFileInfo");
WinampSetExtendedFileInfo delFunc = (WinampSetExtendedFileInfo)
Marshal.GetDelegateForFunctionPointer(
pFunc, typeof(WinampSetExtendedFileInfo));
delFunc(filename, "title", "Whip the Llama's Ass");
WinampMetadata classThe public class WinampMetadata : IDisposable
{
public WinampMetadata()
public int PluginCount { get; }
public string Extensions { get; }
public bool InitPlugins(DirectoryInfo dir, IntPtr hWndOwner)
public void ClosePlugins()
public bool InitFileInfo(FileInfo file)
public bool GetFileInfo(MetaDataType dataType, out string value)
public bool SetFileInfo(MetaDataType dataType, string value)
public bool WriteFileInfo()
public bool GetFileInfoBatch(out MetaData metaData)
public bool GetFileInfoBatch2(out MetaData2 metaData)
public bool SetFileInfoBatch(MetaData metaData)
public void InfoBox(IntPtr hWnd)
public void ConfigPlugin(IntPtr hWnd)
public void AboutPlugin(IntPtr hWnd)
public void Dispose()
}
public enum MetaDataType
{
Artist = 0,
Album,
Track,
Title,
Year,
Comment,
Genre,
Length, // length of song in milliseconds
Bitrate, // in kbps if vbr, returns avg bitrate
SampleRate, // sampling frequency
Stereo, // 1 if stereo, 0 otherwise
VBR // if is vbr number of frames, else 0
}
In most cases, you will want to work with complete information. The public struct MetaData
{
public string Artist;
public string Album;
public string Track;
public string Title;
public string Year;
public string Comment;
public string Genre;
public static MetaData Empty = new MetaData();
}
public struct MetaData2
{
public string Length;
public string Bitrate;
public string SampleRate;
public string Stereo;
public string VBR;
}
The Using the codeThe download contains an usercontrol that uses a Points of interestThe older ANSI plug-ins were self-contained. The newer versions depend on common (nscrt.dll) and individual (libFLAC, libmp4v2) libraries. Some of these libraries reside in Winamp's installation folder and must be copied to the folder of the executing assembly. This complicates the testing of multiple plug-in versions, as the correct library must be in the EXE folder. You can download previous Winamp installers here, to try older plug-in versions. History
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||