How to manage custom DSP effects |
|
The purpose of custom DSP effects, implemented through a set of methods of the Effects COM object, is to give the developer the possibility to apply DSP algorithms to the sound under editing: the concept of DSP effects is quite similar to the concept of Steinberg's Virtual Studio Technology (VST) effects but with some simplification and with more expansion possibilities.
As for VST effects, the main purpose of custom DSP effects is to take a stream of audio data, apply a process to the audio and return the result to the container application: as a secondary purpose, custom DSP effects can be used to perform sound analysis, allowing for example the separation of stereo audio channels into different output files.
The core functionality of DSP effects is the callback function: this function is called by the DSP system and receives a buffer, containing sound samples, a parameter which tells the size in bytes of the buffer and a third parameter that allows passing to the callback a user-defined value. Samples inside the buffer can be in the following formats:
• | If loaded sound file is 8 bits per sample, each sample is represented by a signed integer on 1 byte whose value could be in the range from -127 to 127 |
• | If loaded sound file is 16 bits per sample, each sample is represented by a signed integer on 2 bytes whose value can be in the range from -32768 to 32767 |
• | Whichever the resolution of the loaded sound file, after a call to the Effects.CustomDspUseFloatSamples method each sample is represented by a floating point on 4 bytes whose value can be in the range from -1.0 to 1.0; values are not automatically capped so you could also get values outside of this range |
In all cases described above, for stereo files the sequence of samples will be left channel first followed by right channel. See below a C++ sample of a callback that, after a call to the Effects.CustomDspUseFloatSamples method, will process a stereo file in order to swap the left channel with the right channel:
void WINAPI fnSwapStereoChannels (void *bufferSamples, DWORD bufferSamplesLength, DWORD dwUserData) { // cast the buffer to floating point float *buffTemp = (float *) bufferSamples;
// calculate total number of samples passed to the callback DWORD nTotalSamples = bufferSamplesLength / sizeof (float);
// loop through the buffer DWORD index; for (index = 0; index < nTotalSamples; index +=2) { // swap left channel sample with right channel sample float fSample = buffTemp[index]; buffTemp[index] = buffTemp[index+1]; buffTemp[index+1] = fSample; } } |
In case you should apply more than one custom DSP at the same time, it's important to remember that the type used for managing samples must always match: it's not allowed applying custom DSPs using floating point samples and custom DSPs using integer samples at the same time.
There are two types of custom DSP effects:
• | Internal DSP effects |
The code of these effects is inserted directly inside the code of the container application: usually the DSP code will be limited to a callback function containing the DSP algorithm and to some variable that will store parameters used by the DSP algorithm itself.
In order to initialize an internal DSP effect, you need to create a unique identifier for the internal DSP effect: this task can be achieved through a call to the Effects.CustomDspInternalLoad method: the return value of this call will be a 32 bits value that will identify the internal DSP effect.
At this point you need to set the callback function that will apply the DSP algorithm to incoming sound data: for this purpose you need to create a specific callback function inside your code and then to pass this function to Active Sound Editor calling the Effects.CustomDspInternalSetFunction method. It's important to remember that the algorithm of the DSP effect inside the callback function must be as quick as possible and should never call functions or APIs that could require user's interaction like message boxes and similar.
VERY IMPORTANT WARNING for VB6 users
The Microsoft Visual Basic 6 IDE (Integrated Development Environment) is not safe when dealing with callbacks and secondary threads and could sometime cause unpredictable GPFs or dead-lock situations so it's highly recommended to avoid executing internal DSP code from the VB6 IDE directly or you may experience a crash of VB6 itself. In order to test the code of the DSP, please, compile the EXE of the application and launch the EXE from outside of VB6.
|
• | External DSP effects |
As for VST effects, the code of external DSP effects is inserted inside external dynamic-link library (DLL) files which need to export one or more pre-defined functions.
In order to initialize an external DSP effect, you need to create a unique identifier for the external DSP effect: this task can be achieved through a call to the Effects.CustomDspExternalLoad method which will receive the filename or the absolute pathname of the external DLL file and will return a 32 bits value that will identify the external DSP effect: this method can be considered a specialized version of the LoadLibrary Windows API.
Once we have successfully loaded the external DSP and obtained its unique identifier, we can gain access to its exported functions through the Effects.CustomDspExternalSetFunction method which can be considered a specialized version of the GetProcAddress Windows API.
Functions exported by the dynamic-link library (DLL) containing the DSP effect should be one or more of the following:
• | The callback function used to apply the DSP algorithm to incoming sound data: the presence of this function should be considered as mandatory. The callback function is called internally after invoking the Effects.CustomDspApply method: it's important to note that the code of the callback function must be as quick as possible and should never call functions or APIs that could require user's interaction like message boxes and similar. |
• | Two function used to obtain and set parameters used by the algorithm of the external DSP effect. Calls to these functions at runtime are wrapped within calls to the Effects.CustomDspExternalGetParameters and Effects.CustomDspExternalSetParameters methods. As you may understand, each DSP effect will come with its own set of parameters so they will need to be defined by the DSP developer himself and made public in order to be managed by our container application: just to make a comparison with the Microsoft's SDK, the developer that will write a DSP effect will have to deliver a sort of ".h" include file containing the definition of all of the needed data structures allowing their use from within the container application. |
• | The function used to send custom commands to the external DSP. This is an optional function and its call at runtime is wrapped within a call to the Effects.CustomDspExternalSendCommand method. This function allows the container application sending pre-defined custom commands to the DSP effect in form of a string of characters: as you may understand this gives great flexibility and allows the developer of the DSP effect to define a "dialect" of custom commands. There is no limitation to the way these custom commands could be defined or used: they could be seen as a sort of "command line" where you could define your own set of options and parameters or as a XML formatted string containing both commands and parameters: parsing contents of these commands will be obviously responsibility of the external custom DSP effect. After receiving a custom command, the DSP effect will have the possibility to immediately reply through the return value of the Effects.CustomDspExternalSendCommand method or, in case of a lengthy operation managed inside a secondary thread, at a later time through the use of the PostMessage Windows API, sending a user-defined message to the window (HWND) of the container form or dialog box. |
• | The function used to display or hide the editor (user interface) of the external DSP: editors can be useful in order to display custom user interfaces which allows modifying the algorithm parameters through user's interaction: its presence can be certainly considered as optional. The call to this function at runtime is wrapped within a call to the Effects.CustomDspExternalEditorShow method. |
• | The function used to obtain information about the editor (if available) of the external DSP. The call to this function at runtime is wrapped within a call to the Effects.CustomDspExternalEditorGetInfo method. Typical editor's parameters could be its visibility state, its coordinates and dimensions respect to the client area of the parent window but also colors for customizing the appearance of the editor. |
It's certainly worth to mention that an external dynamic-link library (DLL) could contain more than one DSP effect: in this case, for each of the available DSP effects you will have to call the Effects.CustomDspExternalLoad method (this will obviously generate a specific unique identifier) and the Effects.CustomDspExternalSetFunction method for setting the specific callback function.
Both internal and external custom DSP effects can be discarded from memory using the Effects.CustomDspFree method.
In order to have a better understanding about the use and implementation of custom DSP effects, you will find some sample of usage of these internal and external DSP effects inside the SoundEditor sample installed by the product's setup package. You will also find two projects, written in Visual C++ 6, that demonstrate the implementation of two external custom DSP effects, one containing an editor (named MyCustomDspWithUI) that will implement a simple "Bass Boost" effect and one without any editor (named MyCustomDsp) that will implement two different effects: a simple "Reverb" effect and a simple "Balance" effect.