BASS.NET API for the Un4seen BASS Audio LibraryDSPPROC DelegateBASS.NET API for the Un4seen BASS Audio Library
User defined DSP callback function (to be used with BASS_ChannelSetDSP(Int32, DSPPROC, IntPtr, Int32)).

Namespace: Un4seen.Bass
Assembly: Bass.Net (in Bass.Net.dll) Version: 2.4.12.0
Syntax

public delegate void DSPPROC(
	int handle,
	int channel,
	IntPtr buffer,
	int length,
	IntPtr user
)

Parameters

handle
Type: SystemInt32
The DSP handle (as returned by BASS_ChannelSetDSP(Int32, DSPPROC, IntPtr, Int32)).
channel
Type: SystemInt32
Channel that the DSP is being applied to.
buffer
Type: SystemIntPtr
The pointer to the buffer to apply the DSP to. The sample data is in standard Windows PCM format - 8-bit samples are unsigned, 16-bit samples are signed, 32-bit floating-point samples range from -1 to 1 (not clipped, so can actually be outside this range).
length
Type: SystemInt32
The number of bytes to process.
user
Type: SystemIntPtr
The user instance data given when BASS_ChannelSetDSP(Int32, DSPPROC, IntPtr, Int32) was called.
Remarks

A DSP function should obviously be as quick as possible... playing streams, MOD musics and other DSP functions can not be processed until it has finished.

Some functions can cause problems if called from within a DSP (or stream) function. Do not call these functions from within a DSP callback:

BASS_Stop, BASS_Free, BASS_MusicLoad(String, Int64, Int32, BASSFlag, Int32), BASS_StreamCreate(Int32, Int32, BASSFlag, STREAMPROC, IntPtr) (or any other stream creation functions).

Also, do not call BASS_ChannelRemoveDSP(Int32, Int32) with the same DSP handle as received by the callback, or BASS_ChannelStop(Int32), BASS_MusicFree(Int32), BASS_StreamFree(Int32) with the same channel handle as received by the callback.

If the BASS_CONFIG_FLOATDSP config option is set, then DSP callback functions will always be passed 32-bit floating-point sample data, regardless of what the channels' actual sample format is.

It is clever to NOT alloc buffer data (e.g. a float[]) everytime within the callback method, since ALL callbacks should be really fast! And if you would do a 'float[] data = new float[]' every time here...the GarbageCollector would never really clean up that memory. Sideeffects might occure, due to the fact, that BASS will call this callback too fast and too often... However, this is not always the case, so in most examples it'll work just fine - but if you got problems - try moving any memory allocation things outside any callbacks.

NOTE: When you pass an instance of a callback delegate to one of the BASS functions, this delegate object will not be reference counted. This means .NET would not know, that it might still being used by BASS. The Garbage Collector might (re)move the delegate instance, if the variable holding the delegate is not declared as global. So make sure to always keep your delegate instance in a variable which lives as long as BASS needs it, e.g. use a global variable or member.

Examples

You might use this code as a starting reference (it assumes 32-bit floating-point sample data):
VB
Private _myDSPAddr As DSPPROC  ' make it global, so that the GC can not remove it
Private _data As Single() = Nothing  ' our local buffer
...
_myDSPAddr = New DSPPROC(AddressOf MyDSPCallback)
Bass.BASS_ChannelSetDSP(_stream, _myDSPAddr, IntPtr.Zero, 2)
...
Private Sub MyGain(handle As Integer, channel As Integer, 
                   buffer As IntPtr, length As Integer, user As IntPtr)
  If length = 0 OrElse buffer = IntPtr.Zero Then
    Return
  End If
  ' number of bytes in 32-bit floats, since length is in bytes
  Dim l4 As Integer = length / 4
  ' increase the data buffer as needed
  If _data Is Nothing OrElse _data.Length < l4 Then
    _data = New Single(l4) {}
  End If
  ' copy from unmanaged to managed memory
  Marshal.Copy(buffer, _data, 0, l4)

  // ... do your processing here

  ' copy back from unmanaged to managed memory
  Marshal.Copy(_data, 0, buffer, l4)
End Sub
private DSPPROC _myDSPAddr; // make it global, so that the GC can not remove it
private float[] _data; // local data buffer
...
_myDSPAddr = new DSPPROC(MyDSPCallback);
Bass.BASS_ChannelSetDSP(_stream, _myDSPAddr, IntPtr.Zero, 2);
...
private void MyDSPCallback(int handle, int channel, IntPtr buffer, int length, IntPtr user)
{
  if (length == 0 || buffer == IntPtr.Zero)
    return;
  // number of bytes in 32-bit floats, since length is in bytes
  int l4 = length/4;
  // increase the data buffer as needed
  if (_data == null || _data.Length < l4)
    _data = new float[l4];
  // copy from managed to unmanaged memory
  Marshal.Copy(buffer, _data, 0, l4);

  // ... do your processing here

  // copy back from unmanaged to managed memory
  Marshal.Copy(_data, 0, buffer, l4);
}
If you're using C# you might even use unsafe code blocks to directly access memory via pointers, like in C/C++:

Note: such application must be compiled using the /unsafe compiler option!

myDSPAddr = new DSPPROC(MyDSPGainUnsafe);
Bass.BASS_ChannelSetDSP(_stream, myDSPAddr, IntPtr.Zero, 2);
...
// the unsafe callback
private DSPPROC myDSPAddr; // make it global, so that the Garbage Collector can not remove it
private void MyDSPGainUnsafe(int handle, int channel, IntPtr buffer, int length, IntPtr user)
{
  if (_gainAmplification == 1f || length == 0 || buffer == IntPtr.Zero)
    return;
  // length is in bytes, so the number of floats to process is:
  // length/4 : byte = 8-bit, float = 32-bit
  int l4 = length/4;
  unsafe
  {
    float *data = (float*)buffer;
    for (int a=0; a<l4; a++)
    {
      data[a] *= _gainAmplification;
    }
  }
}
Using unsafe code is fast and efficient (especially in DSP routines), but is not type safe (e.g. no overflow handling, no type checking etc.) - so you just need to know what you are doing.
See Also

Reference