Plugins3 Compilers have been tested with the SDK:
http://msdn.microsoft.com/vstudio/
The SDK was written using this compiler.
The Visual C++ Express
version is free, but requires you to download
Windows Platform SDK yourself.
Also cheap for Students is the Academic version. Shop around to get the
best price.
Code::Blocks integrated development environment.
This is good, free, modern compiler.
This compiler is too old now, it does not work with this SDK.
To help convert your modules to the new SDK, SE includes a simple code generator. On your existing module... Right-click->Build Code Skeleton. This creates the template code for your module in the folder "C:\SE\My Projects\modules_source" - you need to create that folder first.
The template doesn't do any processing, it's just a non-functioning module with the pins and module-name etc setup for you, to save you typing the boring bits. NOTE - SE SDK3 does not yet support auto-duplicating pins, and does support GUI DT_ENUM pins. Avoid any modules with those for the time being.
SynthEdit supports two types of windows.
These are the plain regular windows as used in most applications. They are
always rectangular and always obscure any window 'behind' them.
Advantages
Created to support non-rectangular shapes, these are still actually rectangular but support transparent pixels. Here the red circle is partially transparent.
Advantages:
Disadvantages
Specify which type by deriving your from the appropriate base class. In your graphics class header file, and also in your XML file...
For a native window...
MyPluginGui.h
class MyGui : public SeGuiWindowsGfxBase
{
MyPlugin.xml
<Plugin id="SynthEdit Scope3" graphicsApi="HWND">
class MyGui : public SeGuiCompositedGfxBase
{
<Plugin id="SynthEdit Scope3" graphicsApi="Composited">
You can customize SynthEdit's right-click context menu.
In your .h file...
virtual int32_t MP_STDCALL onCreateContextMenu();
virtual int32_t MP_STDCALL onContextMenu( int32_t selection );
In your .cpp file...
// Add custom items to right-click menu.
int32_t MyGui::onCreateContextMenu()
{
getHost()->addContextMenuItem( L"Cat", 0, 0 );
getHost()->addContextMenuItem( L"Dog", 1, 0 );
return gmpi::MP_OK;
}
// act on user slelecting right-click item.
int32_t MyGui::onContextMenu( int32_t selection )
{
switch( selection )
{
case 0:
// 'Cat' selected
break;
case 1:
// 'Dog' selected
break;
};
return gmpi::MP_OK;
}
To override SynthEdit's right-click handler. NOTE: If possible please avoid doing this as it disables SynthEdit's pop up menu system.
In your .h file...
// This handles the Windows message loop.
// Add here to override right-click behaviour.
virtual LRESULT MsgProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
int32_t onRButtonDown( UINT flags, POINT point );
int32_t onRButtonUp( UINT flags, POINT point );
In your .cpp file...
// Test overriding right-click handling.
// WARNING: This will disable SynthEdit's right-click menu. If possible please avoid taking
// right-click for your own use.
LRESULT MyGui::MsgProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
switch (message)
{
case (WM_RBUTTONDOWN):
{
POINT p;
p.x = MAKEPOINTS(lParam).x;
p.y = MAKEPOINTS(lParam).y;
onRButtonDown( (UINT) wParam, p );
}
return 1;
break;
case (WM_RBUTTONUP):
{
POINT p;
p.x = MAKEPOINTS(lParam).x;
p.y = MAKEPOINTS(lParam).y;
onRButtonUp( (UINT) wParam, p );
}
return 1;
break;
default:
return SeGuiWindowsGfxBase::MsgProc( hwnd, message, wParam, lParam );
break;
}
}
int32_t MyGui::onRButtonDown( UINT flags, POINT point )
{
return gmpi::MP_OK;
}
int32_t MyGui::onRButtonUp( UINT flags, POINT point )
{
return gmpi::MP_OK;
}
The old SEM SDK described modules and pins in code...
// describe the pins (plugs)
bool Module::getPinProperties (long index, SEPinProperties* properties)
{
switch( index ) // !!TODO!! list your in / out plugs
{
// 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 = "8";
break;
SDK3 introduces an easier, compact format based on XML...
<Audio>
<Pin id="0" name="Input" direction="in" datatype="float" rate="audio" default="8"/>
This new method results in smaller executable files and is easier to add new features without breaking old code.
XML Plugin Properties
id
name
category
helpUrl
graphicsApi{HWND,composited,none}
polyphonicSource{ true, false }
polyphonicAggregator{ true, false }
cloned{ true, false }
voiceMonitorIgnore{ true, false }
GUI
Parameters
Pins
XML Pin Properties
id
name
datatype {float, int, text, blob, midi, bool, enum (DSP only. not fully supported)}
default
direction {in,out}
rate {audio} - float pins only.
private{ true, false }
autoRename{ true, false }
isFilename{ true, false }
linearInput{ true, false }
ignorePatchChange{ true, false }
autoDuplicate{ true, false }
isMinimised{ true, false }
isPolyphonic{ true, false }
autoConfigureParameter{ true, false }
parameterId
parameterField{ Value, writeable only in SynthEdit environment -ShortName , MenuItems,MenuSelection,RangeMinimum,RangeMaximum,EnumList,FileExtension
IgnoreProgramChange,Private,Automation,Automation Sysex,Default,Grab,Normalized
}
metadata
notes
hostConnect
SDK Version 3 is designed to control the host in a very natural way. You put a
special pin on your GUI Module class, and specify what you want to connect it to
(on the host).
Think of the host itself being like a SEM with pins exposed for various features...
For example the keyboard2 module connects to the host's "Voice/Pitch" pin like so...
<Pin id="0" name="Pitch" direction="in" datatype="float" hostConnect="Voice/Pitch" />
- Put an DT_INT pin on your GUI Module.
- specify - hostConnect="PatchCommands".
- Valid commands are:
0 null
1 CopyPatch
2 LoadPatch
3 SavePatch
4 LoadBank
5 SaveBank
To initiate a bank load. Set your pin to 4, then back to zero (ready for the next command). SynthEdit will display the Bank-Load dialog box, then load whatever bank the user selects.
Although Version3 has been re-written from scratch, it's not much different from the older SDK. Some function names have changed, most only slightly to reflect modern naming conventions. Some features like sleep-mode are now more integrated into the SDK to save you time.
The easiest way to get your module ported to the new SDK is with the Skeleton Code Generator. The code generator produces a basic template module. It contains only the basic code to generate a module with the correct pins and some standard methods. You can then copy over your signal processing code from your old module.


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;
}
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"; // !!TODO!!
// return a unique string 32 characters max
properties->id = "Synthedit Gain Example"; // !!TODO!!
properties->about = "by Jeff M (MS)" ;
return true;
}
// describe the pins (plugs)
bool Module::getPinProperties (long index, SEPinProperties* properties)
{
switch( index ) // !!TODO!! list your in / out plugs
{
// 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:
return false; // 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" default="0"/>
<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.
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.
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() 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);
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);
That was the quick guide to porting to SDK3. Any further questions please post to the SynthEdit SDK group at yahoo.
SEM is an implementation of GMPI (Generalized Music Plugin Interface) with a few extensions for SynthEdit. Some less crucial GMPI features are not supported yet.