How to access BASS library functionalities directly |
|
For some of its many features, Audio Sound Editor API for .NET embeds the well known BASS library and some of its plugins which allow decoding and playing a number of different audio formats; in some case you could have the need to drive BASS directly in order to perform some particular task not available from the component itself; for achieving this possibility you would have to load and eventually free BASS or its plugins through the LoadLibrary or LoadLibraryEx and the FreeLibrary Windows API functions.
Due to the fact that BASS.DLL and some of its plugins are already embedded inside this component, you don't need to redistribute them with your application because you can get direct access to the module loaded in memory through its handle (HMODULE) returned by an internal call to the LoadLibrary Windows API: this handle, for usage with your own code, can be obtained through the BassModuleGet method.
Through this method you have the possibility to access instances already loaded in memory by the multimedia engine AdjMmsEng.dll: indeed, by knowing the HMODULE of BASS.DLL or of some of its plugins, you could get the pointer to all of the functions exported by the various BASS modules through the GetProcAddress Windows API.
For details about dynamic loading of functions exported by the various BASS modules you can refer to the official documentation of BASS.
IMPORTANT NOTE: Calls to the BASS_Init and BASS_Free functions of BASS.DLL are already managed internally by multimedia engine AdjMmsEng.dll so it's recommended avoiding their usage in order to avoid compatibility issues.
Let's see a small example in C# and VB.NET code where we need to access, directly from our code, the BASS_ChannelSetAttribute function exported by BASS.DLL: In order to access these functions directly from your code, you will need to make some modification to your code; for gaining access to the GetProcAddress Windows API, you need to add the following statemens:
Visual Basic .NET |
Imports System.Runtime.InteropServices
<DllImport("kernel32.dll")> _ Public Shared Function GetProcAddress(ByVal hModule As IntPtr, ByVal procedureName As String) As IntPtr End Function
|
Visual C# |
using System.Runtime.InteropServices;
[DllImport("kernel32.dll")] public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
|
now we need to define the prototype of the BASS_ChannelSetAttribute function exported by BASS.DLL
Visual Basic .NET |
<UnmanagedFunctionPointer(CallingConvention.Cdecl)> _ Private Delegate Function funcBASS_ChannelSetAttribute(ByVal handle As Int32, ByVal attrib As Int32, ByVal value As Single) As Int32
Private BASS_ChannelSetAttribute As funcBASS_ChannelSetAttribute
|
Visual C# |
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate Int32 funcBASS_ChannelSetAttribute(Int32 handle, Int32 attrib, float value);
funcBASS_ChannelSetAttribute BASS_ChannelSetAttribute;
|
at this point we use GetProcAddress to get the address of the BASS_ChannelSetAttribute function, and we use the returned value in conjunction with the GetDelegateForFunctionPointer static method within the Marshal class to assign this address to a delegate that we have just defined.
Visual Basic .NET |
Private Sub LoadBassFunction() Dim hModule As Int32 = 0 m_audioSoundEditorApi.BassModuleGet(enumBassModules.MODULE_BASS, hModule)
BASS_ChannelSetAttribute = CType(Marshal.GetDelegateForFunctionPointer( _ GetProcAddress(New IntPtr(hModule), "BASS_ChannelSetAttribute"), _ GetType(funcBASS_ChannelSetAttribute)), funcBASS_ChannelSetAttribute) End Sub
|
Visual C# |
private void LoadBassFunction() { Int32 hModule = 0; m_audioSoundEditorApi.BassModuleGet(enumBassModules.MODULE_BASS, ref hModule); BASS_ChannelSetAttribute = (funcBASS_ChannelSetAttribute) Marshal.GetDelegateForFunctionPointer( GetProcAddress((IntPtr)hModule, "BASS_ChannelSetAttribute"), typeof(funcBASS_ChannelSetAttribute)); }
|
That's all: we can now call the BASS function directly from our code: if for example we should need to lower a stream to half its volume, we could simply use the following code:
Visual Basic .NET |
Private Sub button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles button1.Click Dim hStream As Int32 = 0 m_audioSoundEditorApi.BassStreamGet(0, hStream) BASS_ChannelSetAttribute(hStream, BASS_ATTRIB_VOL, 0.5f) End Sub
|
Visual C# |
private void button1_Click(object sender, EventArgs e) { Int32 hStream = 0; m_audioSoundEditorApi.BassStreamGet(0, ref hStream); BASS_ChannelSetAttribute(hStream, BASS_ATTRIB_VOL, 0.5f); }
|
This brings another interesting point: on the function call above we are acting on a playback stream, identified by the hStream variable: how can we obtain that stream? This is quite simple: In case you should need to have direct access to the handle (HSTREAM) of a playback stream created through BASS after calling the PlaySound or PlaySoundRange methods, you can use the BassStreamGet method.
A second feature brought by the BassStreamGet method is the possibility to apply custom DSP effects implemented by third-party plugins which work on playback streams of type HSTREAM: some of these plugins, such as BASS_WA, BASS_WINAMP, BASS_SFX and others, are available on the BASS web site: please, refer to the respective documentations for knowing how to use the HSTREAM playback stream with these plugins.
As mentioned before, Audio Sound Editor API for .NET already embeds some external BASS plugin that supports the decoding of specific audio formats like WMA, MP4, AC3 and others: from time to time someone belonging to the BASS community delivers new plugins for supporting other audio formats; also if not directly embedded inside the component, functions exported by the plugin for creating the playback audio stream can be in any case accessed by the component through a previous call to the BassPluginStreamCreateFunc method which allows loading the plugin and the specific function dynamically at runtime.
After calling the BassPluginStreamCreateFunc method, when the LoadSound or LoadSoundFromMemory) methods are invoked, the function exported by the plugin will be automatically used for trying to load the sound file.
At the moment the component supports the dynamic loading of functions for creating playback streams from sound files only: the related function is usually exported by the plugin with the name BASS_xxx_StreamCreateFile where "xxx" indicates the managed audio format, for example BASS_AIX_StreamCreateFile or BASS_TTA_StreamCreateFile; the exported function must use the following C syntax:
HSTREAM BASS_xxx_StreamCreateFile ( BOOL mem, const void *file, QWORD offset, QWORD length, DWORD flags );
|
It's important to note that you don't need calling the LoadLibrary and FreeLibrary functions on the DLL of the plugin: these functions are already called internally by the component when the BassPluginStreamCreateFunc method is invoked: you simply need to provide the pathname of the plugin and the component will take care of the needed operations.