Upgrading from SDK Version 2
Example: Porting SE-Gain
- Start SynthEdit.
- Insert the modules you want to upgrade.
- Insert->My Modules->Gain Example.
- right-click->Build Code Skeleton. A dialog will appear.
- Type the name of your module: 'Gain2'.
- Click 'OK' to generate the code.
- Browse to your Documents folder.
- Find the 'new-module' folder. This contains the new SEM project.
- Rename that folder to the module name - 'Gain2'.
- you may need to move that folder to you SDK folder, so it is next to the SDK files.
- Open the project in Visual Studio (you may get a Project conversion wizard dialog).
- Build the project.
You should now be able to re-open SynthEdit and insert your new module. (It is not functional yet because it has an empty sub-process method).
Back in Visual Studio you can inspect the new project files. There are a handful of SDK files, plus the actual module files: Gain.h, Gain.cpp, and Gain.xml.
Open Gain2.cpp, inside is the minimal code for your plugin, including a basic subProcess method.
void Gain2::subProcess( int bufferOffset, int sampleFrames ) { // get pointers to in/output buffers. float* input = bufferOffset + pinInput.getBuffer(); float* input2 = bufferOffset + pinInput2.getBuffer(); float* output = bufferOffset + pinOutput.getBuffer(); for( int s = sampleFrames; s > 0; --s ) { // TODO: Signal processing goes here. // Increment buffer pointers. ++input; ++input2; ++output; } }
Note the name has changed from 'sub_process' to 'subProcess', this is an example of the updated coding conventions. Also the buffer pointers are retrieved differently. Otherwise the method is the same as before.
The TODO is a reminder to copy your signal processing code from your old module...
for( int s = sampleFrames; s > 0; --s )
{
float in1 = *input; // get the sample 'POINTED TO' by in1
float in2 = *input2;
// do the actual processing (multiplying the two input samples together)
float result = in1 * in2;
*output = result; // store the result in the output buffer
// Increment buffer pointers.
++input;
++input2;
++output;
}
What happened to getModuleProperties() and getPinProperties()?
In the old SDK you had 2 methods to provide your module name and pin names and properties...
// describe your module
bool Module::getModuleProperties (SEModuleProperties* properties)
{
// describe the plugin, this is the name the end-user will see.
properties->name = "Gain Example";
// return a unique string 32 characters max
properties->id = "SynthEdit Gain Example";
properties->about = "by Jeff M (MS)" ;
return true;
}
// describe the pins (plugs)
bool Module::getPinProperties (long index, SEPinProperties* properties)
{
switch( index )
{
// typical input plug (inputs are listed first)
case 0:
properties->name = "Input";
properties->variable_address = &input1_buffer;
properties->direction = DR_IN;
properties->datatype = DT_FSAMPLE;
properties->default_value = "0";
break;
case 1:
properties->name = "Input";
properties->variable_address = &input2_buffer;
properties->direction = DR_IN;
properties->datatype = DT_FSAMPLE;
properties->default_value = "5";
break;
// typical output plug
case 2:
properties->name = "Output";
properties->variable_address = &output1_buffer;
properties->direction = DR_OUT;
properties->datatype = DT_FSAMPLE;
break;
default:
returnfalse; // host will ask for plugs 0,1,2,3 etc. return false to signal when done
};
return true;
}
These methods are gone. In their place is a simple text file - Gain2.xml.
<?xml version="1.0" encoding="utf-8" ?> <PluginList> <Plugin id="My Gain2" name="Gain2" category="MyModules" graphicsApi="HWND" helpUrl="Gain2.htm"> <Audio> <Pin id="0" name="Input" direction="in" datatype="float" rate="audio"/> <Pin id="1" name="Input 2" direction="in" datatype="float" rate="audio" default="0.5"/> <Pin id="2" name="Output" direction="out" datatype="float" rate="audio"/> </Audio> </Plugin> </PluginList>
As you can see this is less typing and easy to edit. It also results in faster compilation, less code generated and smaller SEM files.
OnPlugStateChange
This method was called anytime a pin was updated.
void Module::OnPlugStateChange(SEPin *pin)
{
state_type in_stat1 = getPin(PN_INPUT1)->getStatus();
SDK3 replaces OnPlugStateChange() with onSetPins().
void Gain2::onSetPins(void)
{
// Check which pins are updated.
if( pinInput.isStreaming() )
{
}
Rather than check inputs for a status of ST_RUN, you now use the isStreaming() method.
Rather than check non-audio pins for ST_ONE_OFF, you now use the isUpdated() method.
void Gain2::onSetPins(void)
{
// Check which pins are updated.
if( pinInput.isUpdated() && pinInput2.isUpdated() )
{
// They both changed at the same time.
}
The functionality is the same as before, just a more readable syntax.
The only substantial difference is - When two pins are updated at the same time, onSetPins() is called only once (with both pins flagged). It was very difficult before to detect simultaneous updates on two pins, now it's easy.
Likewise, when the audio engine starts SynthEdit flags all your input pins as 'updated' and calls onSetPins() just the once. This results in less function call overhead and faster SEMs.
SET_PROCESS_FUNC
This has been updated. Search-and-Replace your old code with the new look. Don't forget the '&' (address-of) symbol.
Old.
SET_PROCESS_FUNC(Module::sub_process);
New.
SET_PROCESS(&Gain2::subProcess);
TransmitStatusChange
TransmitStatusChange() has been replaced with setStreaming(). Less typing, more readable...
Old
getPin(PN_OUTPUT1)->TransmitStatusChange( SampleClock(), ST_RUN );
New
pinOutput.setStreaming(true);
Old
getPin(PN_OUTPUT1)->TransmitStatusChange( SampleClock(), ST_STATIC );
New
pinOutput.setStreaming(false);
Notes:
- Audio pins default to streaming=true at startup.
- SDK3 is stricter than SDK2. You can't set an output pin's value or streaming state untill your module has started processing. i.e. you can't use setStreaming() in your constructor, not in open() either. For an alternative see onGraphStart().
Sleep Mode
Previously, after checking your input pins were silent, you implemented sleep mode with a special process method to filled the output buffers with silent samples. Once that was done you asked the host to put the module to sleep...
// every module has an output buffer of approx. 100 samples.
// before deactivating module, need to fill that buffer with silence.
// "static_count" is counting out those 100 samples.
void Module::sub_process_static(long buffer_offset, long sampleFrames )
{
sub_process(buffer_offset, sampleFrames);
static_count = static_count - sampleFrames;
if( static_count <= 0 )
{
CallHost(seaudioMasterSleepMode);
}
}
Although this is still necessary. The new SDK does it for you. Your module no longer needs any special code. You don't need to port that code to SDK V3.
If you don't want automatic sleep mode, or need to handle it manually you can disable automatic sleep mode...
setSleep(false);
Summary
That was the quick guide to porting to SDK3. Any further questions please post to the SynthEdit SDK group at yahoo.