• No results found

Writing Plug-Ins with RackAFX

3.9 Setup Preferences

3.9.4 Volume.h File

Your plug-in code will use the index value 0 (uControlID in the properties dialog) to map to the m_fVolume variable, which is controlled by the slider named “Volume” on the UI.

3.9.4 Volume.h File

Before we add the code, look around the plug-in object fi les (volume.h and volume.cpp) to get a better understanding of what’s going on and what you’ll need to modify. First, open the volume.h fi le and look inside:

// RackAFX abstract base class for DSP fi lters class CVolume : public CPlugIn

{

public: // plug-in API Functions //

Figure 3.13: The completed slider properties.

As you add, edit, or remove controls from the main UI you will notice that RackAFX will fl ash to the compiler and back as it writes the code for you. You might use this fl ashing as a signal that the code update is synchronized. If you don’t like it, minimize the compiler and the fl ashing will not occur. There is a special check-box in View > Preferences to start the compiler minimized for this very reason.

// 1. One Time Initialization CVolume();

<SNIP SNIP SNIP>

// 7. userInterfaceChange() occurs when the user moves a control.

virtual bool userInterfaceChange(int nControlIndex);

// Add your code here: --- //

// END OF USER CODE --- //

// ADDED BY RACKAFX -- DO NOT EDIT THIS CODE!!! --- //

// **--0x07FD--**

fl oat m_fVolume;

// **--0x1A7F--**

// --- //

};

Aside from the main plug-in functions we discussed in Chapter 2 , you will see some more commented areas of code. In the fi rst part marked // Add your code here: you can add more variables or function defi nitions just like you would in any .h fi le. Try to keep your code in the denoted area to make it easier to fi nd and read. The area below that says:

// ADDED BY RACKAFX—DO NOT EDIT THIS CODE!!!

is very important—you will see your member variable m_fVolume declared in this area. This is the portion of the .h fi le that RackAFX modifi es when you add, edit, or delete controls from your control surface. It is imperative that you let RackAFX maintain this part of the C11 code. There are several other portions of code in the .cpp fi le that have similar warnings and interesting hex symbols (0x1A7F, etc.); do not edit the code contained between the hex codes or commented areas.

You will see the notation <SNIP SNIP SNIP> frequently in the printed code as a reminder that code has been cut out for easier reading.

RackAFX writes C++ code for you! But, you have to be careful not to alter the RackAFX C++

code in any way. You can always tell if the code is RackAFX code because there will be warning comments and strange hex codes surrounding the RackAFX code. The RackAFX code is left for you to see only as a backup to your debugging and should never be altered by anyone except RackAFX.

In this case, check to verify that RackAFX added the fl oat member variable m_fVolume as you anticipated. Next, move on to the volume.cpp fi le and have a look at it, starting from the top.

3.9.5 Volume.cpp File

Constructor and destructor

The constructor is the One-Time-Init function and is set up to:

• call initUI(): This is where your GUI controls are initialized; m_fVolume is initialized to 0.75 inside this function. It is important to make sure this remains the fi rst line of the constructor so that your GUI variables are always initialized fi rst.

• Set the plug-in name variable: This is what you will see in the user plug-in menu and on the GUI windows.

• Set the plug-in defaults (snipped out here); you will rarely need to change these variables.

• Give you a place to fi nish any of your own initializations at the end.

The destructor is empty because nothing has been allocated dynamically in this plug-in.

CVolume::CVolume() {

// Added by RackAFX - DO NOT REMOVE //

// Setup the RackAFX UI Control List and Initialize Variables initUI();

// END InitUI

// built in initialization m_PlugInName = "Volume";

// Default to Stereo Operation:

// Change this if you want to support more/less channels

<SNIP SNIP SNIP>

// Finish initializations here }

/* destructor()

Destroy variables allocated in the contructor()

*/

CVolume::~CVolume(void) {

}

prepareForPlay()

There is nothing to write yet since there are no custom controls or other allocations.

processAudioFrame()

This function is where the signal processing action happens. Above the defi nition is a comment block as a reminder of how to get the audio data samples into and out of the I/O buffers. Currently, RackAFX only supports mono and stereo plug-ins. The left and right channels are accessed using the normal array-indexed C11 pointer mechanism. Of special note is the reminder that all values in and out are (and should be) between −1.0 and 11.0.

/* processAudioFrame

// ALL VALUES IN AND OUT ON THE RANGE OF -1.0 TO + 1.0 LEFT INPUT = pInputBuffer[0];

RIGHT INPUT = pInputBuffer[1]

LEFT OUTPUT = pInputBuffer[0]

LEFT OUTPUT = pOutputBuffer[1]

*/

bool __stdcall CVolume::processAudioFrame(fl oat* pInputBuffer, fl oat* pOutputBuffer, UINT uNumInputChannels, UINT uNumOutputChannels)

{

// output = input -- change this for meaningful processing //

// Do LEFT (MONO) Channel; there is always at least one input/one output (INSERT Effect)

pOutputBuffer[0] = pInputBuffer[0];

// Mono-In, Stereo-Out (AUX Effect)

if(uNumInputChannels == 1 && uNumOutputChannels == 2) pOutputBuffer[1] = pInputBuffer[0];

// Stereo-In, Stereo-Out (INSERT Effect)

if(uNumInputChannels == 2 && uNumOutputChannels == 2) pOutputBuffer[1] = pInputBuffer[1];

return true;

}

Take a look at the function as RackAFX wrote it for you—it is designed to pass audio through unmodifi ed. In this case, you simply write the output buffer with the data from the input buffer. In your processAudioFrame() function, get used to always processing the fi rst channel then optionally processing the second one. This makes it easy to write mono/stereo capable plug-ins and will also make it easier to extend when RackAFX has more channel options.

Because the code is already done, you could compile it right now and try it in RackAFX as a sanity check to make sure your audio hardware is set up properly. In fact, we’ll do that right after examining the last few functions in the fi le (I promise you will write code shortly).

userInterfaceChange()

Perhaps the second most important function is userInterfaceChange(), which is called when the user changes a control on the control surface:

/* ADDED BY RACKAFX -- DO NOT EDIT THIS CODE!!! --- //

**--0x2983--**

Variable Name Index

--- m_fVolume 0

--- **--0xFFDD--**

// --- */

// Add your UI Handler code here --- //

//

As with processAudioFrame(), there is a “hint” comment above the function defi nition which reminds you how RackAFX has mapped your variable to a control index. In this case, the m_fVolume variable is mapped to index 0.

bool __stdcall CVolume::userInterfaceChange(int nControlIndex) {

// decode the control index, or delete the switch and use brute force calls switch(nControlIndex)

{

case 0:

{

break;

} default:

break;

}

return true;

}

userInterfaceChange() implements the fi rst part of a switch/case statement in case you need to decode the control index and do something to the data before fi nally altering your code to refl ect the change. Often, you will have nothing to write here either.

Build the plug-in. Since the default plug-in behavior is to simply pass audio unaffected, you can build the project now and test it in RackAFX to make sure everything is working properly. Rebuild your project from the compiler or from RackAFX’s Rebuild button. You should get a clean build with no errors.

At this point, you have built a DLL which is designed to serve up CVolume objects when the client requests them. The problem is that RackAFX doesn’t yet know your CVolume plug-in is available. During the debugging phase, you need to get used to manually loading and unloading the DLL. You do this with the Load button or the toolbar/menu item. After you hit Load, RackAFX calls the appropriate functions to load your DLL into its address space.

You will see the control surface change to refl ect that your plug-in is loaded. You will also see the Load button change to read Unload. When it is time to go back to C11, modify, and recompile, you’ll need to unload the project fi rst so you can reload it in its later state.

Use Audio > Load Audio File to load a test fi le. Then use the transport control buttons to play, loop, and pause or stop the fi le. The volume control should have no effect since we haven’t written any code yet. Make sure you get audio at this point before continuing; if you don’t, check your audio adapter settings.

In order to make the volume control slider work, we need to wire it into the processing code.

The volume slider is directly mapped to the volume coeffi cient m_fVolume; as the slider moves from 0.0 to 1.0, so does the volume coeffi cient. So, the algorithm is simple to write:

just multiply the input audio sample by the volume coeffi cient and set the output to that value.

Switch to your C11 compiler and fi nd the processAudioFrame() function. Modify it by doing the multiplication described above, which implements the difference equation for the fi lter.

bool __stdcall CVolume::processAudioFrame(fl oat* pInputBuffer, fl oat* pOutputBuffer, UINT uNumInputChannels, UINT uNumOutputChannels) {

// output = input -- change this for meaningful processing //

// Do LEFT (MONO) Channel; there is always at least one input/one output // (INSERT Effect)

pOutputBuffer[0] = pInputBuffer[0]*m_fVolume;

// Mono-In, Stereo-Out (AUX Effect)

if(uNumInputChannels == 1 && uNumOutputChannels == 2) pOutputBuffer[1] = pInputBuffer[0]*m_fVolume;

// Stereo-In, Stereo-Out (INSERT Effect)

if(uNumInputChannels == 2 && uNumOutputChannels == 2) pOutputBuffer[1] = pInputBuffer[1]*m_fVolume;

return true;

}

You should always build and test your brand-new project fi rst before modifying any code! You want to do this to make sure there are no C++ errors (you might have inadvertently hit a key or changed some code), as well as to make sure your audio system is working and you can hear the audio data.

Plug-In

processAudioFrame()

= pBuffer[0] PBI9ROXPH

Raw Data: 0.0 to 1.0

Volume 0.75

There are only three lines to modify, one for the fi rst channel and another two for the other routing combinations. The modifi cation is shown in bold where you are scaling the input by the volume coeffi cient. Can you see how this relates to the difference equation? If not, stop now and go back to fi gure it out. Now, rebuild the project and reload it into RackAFX. Try the slider and you will see that the volume does indeed change. Congrats on your fi rst plug-in!

What makes this plug-in so easy and quick to develop is that the slider volume control maps directly to a variable that is used in the processAudioFrame() function, as depicted in Figure 3.14 . This means the data coming from the slider can be used directly in the algorithm.

The data coming from the slider and controlling m_fVolume is said to be raw data . You use the raw value to affect the signal processing algorithm.