Copyright © 2006-2019 MultiMedia Soft

How to access BASS library functionalities directly

Previous pageReturn to chapter overviewNext page

For some of its many features, Audio Sound Recorder 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 and BASS_ChannelGetLevel functions  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 statements:

 

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 and BASS_ChannelGetLevel functions 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

<UnmanagedFunctionPointer(CallingConvention.Cdecl)> _

Private Delegate Function funcBASS_ChannelGetLevel(ByVal handle As Int32) As Int32

 

Private BASS_ChannelSetAttribute As funcBASS_ChannelSetAttribute

Private BASS_ChannelGetLevel As funcBASS_ChannelGetLevel

 

 

Visual C#

 

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]

private delegate Int32 funcBASS_ChannelSetAttribute(Int32 handle, Int32 attrib, float value);

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]

private delegate Int32 funcBASS_ChannelGetLevel(Int32 handle);

 

funcBASS_ChannelSetAttribute BASS_ChannelSetAttribute;

funcBASS_ChannelGetLevel BASS_ChannelGetLevel;

 

 

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 LoadBassFunctions()

 Dim hModule As Int32 = 0

 audioSoundRecorder1.BassModuleGet(enumBassModules.MODULE_BASS, hModule)

 

 BASS_ChannelSetAttribute = CType(Marshal.GetDelegateForFunctionPointer( _

         GetProcAddress(New IntPtr(hModule), "BASS_ChannelSetAttribute"), _

         GetType(funcBASS_ChannelSetAttribute)), funcBASS_ChannelSetAttribute)

 BASS_ChannelGetLevel = CType(Marshal.GetDelegateForFunctionPointer( _

         GetProcAddress(New IntPtr(hModule), "BASS_ChannelGetLevel"), _

         GetType(funcBASS_ChannelGetLevel)), funcBASS_ChannelGetLevel)

End Sub

 

 

Visual C#

 

private void LoadBassFunctions()

{

 Int32 hModule = 0;

 audioSoundRecorder1.BassModuleGet(enumBassModules.MODULE_BASS, ref hModule);

 BASS_ChannelSetAttribute = (funcBASS_ChannelSetAttribute) Marshal.GetDelegateForFunctionPointer(

                                 GetProcAddress((IntPtr)hModule, "BASS_ChannelSetAttribute"),

                                 typeof(funcBASS_ChannelSetAttribute));

 BASS_ChannelGetLevel = (funcBASS_ChannelGetLevel)Marshal.GetDelegateForFunctionPointer(

                                 GetProcAddress((IntPtr)hModule, "BASS_ChannelGetLevel"),

                                 typeof(funcBASS_ChannelGetLevel));

}

 

 

That's all: we can now call the BASS function directly from our code: if for example we should need to lower a playback 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 hStreamPlay As Int32 = 0

 audioSoundRecorder1.BassStreamGet(false, hStreamPlay)

 BASS_ChannelSetAttribute(hStreamPlay, BASS_ATTRIB_VOL, 0.5f)

End Sub

 

 

Visual C#

 

private void button1_Click(object sender, EventArgs e)

{

 Int32        hStreamPlay = 0;

 audioSoundRecorder1.BassStreamGet(false, ref hStreamPlay);

 BASS_ChannelSetAttribute(hStreamPlay, BASS_ATTRIB_VOL, 0.5f);

}

 

 

or if we should need to get the volume level of a recording stream we could do the following:

 

Visual Basic .NET

 

 Dim hStreamRec As Int32 = 0

 audioSoundRecorder1.BassStreamGet(True, hStreamRec)

 Dim dwLevel As Int32 = BASS_ChannelGetLevel (hStreamRec)

 Dim wLeft As Int16 = CShort(Fix(dwLevel))

 Dim wRight As Int16 = CShort(Fix(dwLevel >> 16))

 

 

Visual C#

 

 Int32 hStreamRec = 0;

 audioSoundRecorder1.BassStreamGet(true, ref hStreamRec);

 Int32 dwLevel = BASS_ChannelGetLevel (hStreamRec);

 Int16 wLeft = (Int16)dwLevel;

 Int16 wRight = (Int16)(dwLevel >> 16);

 

 

This brings another interesting point: on the first code snippets above we are acting on a playback stream, identified by the hStreamPlay variable and on the second code snippet we are acting on a recording stream, identified by the hStreamRec variable: how can we obtain those streams? This is quite simple: In case you should need to have direct access to the handle (HSTREAM or HRECORD) of a playback or recording stream created through BASS, you could 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 and recording streams of type HSTREAM and HRECORD: 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 HSTREAM and HRECORD streams with these plugins.

 

As mentioned before, Audio Sound Recorder 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 developers belonging to the BASS community deliver new plugins for supporting other audio formats; also if not directly embedded inside the component, functions exported by the plugin for creating the 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 StartFromFile, StartFromMemory or StartFromURL methods are invoked, the function exported by the plugin will be automatically used for trying to load the sound file or the Internet stream.

 

At the moment the component supports the dynamic loading of functions for creating streams from the following entities:

 

Sound files: 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

);

 

 

Internet streams: the related function is usually exported by the plugin with the name BASS_xxx_StreamCreateURL where "xxx" indicates the managed audio format, for example BASS_AIX_StreamCreateURL; the exported function must use the following C syntax:

 

 

HSTREAM BASS_xxx_StreamCreateURL (

   const char *url,

   DWORD offset,

   DWORD flags,

   DOWNLOADPROC *proc,

   void *user

);

 

 

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.