def checkMinMaxDefault(el): """ check that min, max, and default values are OK """ label = getText(el,'label') min = float(getText(el,'min')) max = float(getText(el,'max')) mapping = getText(el,'mapping') defaultTxt = getText(el,'default') # protection against non-existing defaults in controlouts if ("" != defaultTxt): default = float(defaultTxt) else: default = min if float(min) >= float(max) : print(T+"Error: "+label+" min > max !!!") return 1 elif float(default) > float(max) : print(T+"Error: "+label+" default > max !!!") return 2 elif float(default) < float(min) : print(T+"Error: "+label+" default < min !!!") return 3 elif((mapping=='log') and (min <= 0)): print(T+"Error: "+label+" negative min is not allowed with logarithmic mapping") return 4 else: return 0
def checkMinMaxDefault(el): """ check that min, max, and default values are OK """ label = getText(el, 'label') min = float(getText(el, 'min')) max = float(getText(el, 'max')) mapping = getText(el, 'mapping') defaultTxt = getText(el, 'default') # protection against non-existing defaults in controlouts if ("" != defaultTxt): default = float(defaultTxt) else: default = min if float(min) >= float(max): print(T + "Error: " + label + " min > max !!!") return 1 elif float(default) > float(max): print(T + "Error: " + label + " default > max !!!") return 2 elif float(default) < float(min): print(T + "Error: " + label + " default < min !!!") return 3 elif ((mapping == 'log') and (min <= 0)): print(T + "Error: " + label + " negative min is not allowed with logarithmic mapping") return 4 else: return 0
def writeUpdateStates(fc, states): if ([] != states): fc.write(T + "// Update the states in plugin's structure:" + '\n') for state_node in states: state_label = getText(state_node, 'label') state_type = getText(state_node, 'type') fc.write(T + 'plugin_data->' + state_label + ' = ' + state_label + ';' + '\n') return
def writeGetStates(fc, states): if ([] != states): fc.write(T + "// Get the states from plugin's structure:" + '\n') for state_node in states: state_label = getText(state_node, 'label') state_type = getText(state_node, 'type') fc.write(T + state_type + ' ' + state_label + ' = plugin_data->' + state_label + ';' + '\n') return
def writeCallbackCode(fc, callback): if callback: code = getText(callback, 'code') label = getText(callback, 'label') fc.write(T + '// Code from callback <' + label + '> of XSPIF meta-plugin' + '\n') fc.write(T + '{' + '\n') fc.write(code) fc.write('\n' + T + '}' + '\n') return
def writeUpdateStates(fc, states): if ([] != states): fc.write('\n') fc.write(T + "// Update the states in plugin's structure:" + '\n') for s_node in states: s_label = getText(s_node, 'label') s_type = getText(s_node, 'type') fc.write(T + 'x->' + s_label + ' = ' + s_label + ';' + '\n') return
def writeGetParams(fc, params): if ([] != params): fc.write('\n') fc.write(T + "// Get the params from plugin's structure:" + '\n') for p_node in params: p_label = getText(p_node, 'label') p_type = getText(p_node, 'type') fc.write(T +p_type + ' ' + p_label +' = x->m_f'+p_label+';' + '\n') return
def writeGetStates(fc, states): if ([] != states): fc.write('\n') fc.write(T + "// Get the states from plugin's structure:" + '\n') for s_node in states: s_label = getText(s_node, 'label') s_type = getText(s_node, 'type') fc.write(T + s_type + ' ' + s_label + ' = x->' + s_label + ';' + '\n') return
def writeGetParams(fc, params): if ([] != params): fc.write(T + "// Get the params from plugin's structure:" + '\n') for param_node in params: param_caption = getText(param_node, 'caption') param_label = getText(param_node, 'label') param_type = getText(param_node, 'type') fc.write(T + 'const LADSPA_Data ' + param_label + ' = *(plugin_data->m_pf' + param_label + ');' + '\n') fc.write('\n') return
def writeCallbackCode(fc, callback): if callback: code = getText(callback, 'code') label = getText(callback, 'label') fc.write('\n') fc.write(T + '// Code from callback <' + label + '> of XSPIF meta-plugin' + '\n') fc.write(T + '{' + '\n') fc.write(code) fc.write('\n'+T+ '}' + '\n') return
def writeGetControlouts(fc, controlouts): if ([] != controlouts): fc.write('\n') fc.write(T + "// Get the pointers to the outlets:" + '\n') for c_node in controlouts: c_label = getText(c_node, 'label') c_type = getText(c_node, 'type') fc.write(T + 't_outlet *' + c_label + ' = x->' + c_label + ';' + '\n') fc.write('\n') return
def write(plugin,filename_prefix): fcpp = file(filename_prefix + '.au.cpp','w') xml_filename = filename_prefix + ".xspif" print T+'writing : '+filename_prefix+'.au.cpp' ######## independent code here ############ # Variables plugId = getText(plugin,'plugId') manufId = getText(plugin,'manufId') maker = getText(plugin,'maker') pluginLabel = getText(plugin,'label') pluginLabel.replace(' ', '') #remove spaces in pluginLabel.title(); # forces the first letter to upper case copyright = getText(plugin,'copyright') comment = getText(plugin,'comment')+'\n' pluginCode = getText(plugin,'code')+'\n' pluginCaption = getText(plugin,'caption') params = plugin.getElementsByTagName('param') states = plugin.getElementsByTagName("state") pins = plugin.getElementsByTagName("pin") callbacks = plugin.getElementsByTagName('callback') instantiate = None deinstantiate = None process = None processEvents = None activate = None deactivate = None if callbacks == [] : print T+ "Warning: you haven't implemented any callback"+'\n' else : for callback in callbacks: text = str(getText(callback,'label')) if text == 'instantiate': instantiate = callback if text == 'deinstantiate': deinstantiate = callback if text == 'activate': activate = callback if text == 'deactivate': deactivate = callback if text == 'process': process = callback if text == 'processEvents': print 'XSPIF Warning: callback "processEvents" not implemented in VST for now' #processEvents = callback instantiateCode = getText(instantiate,'code') deinstantiateCode = getText(deinstantiate,'code') activateCode = getText(activate,'code') deactivateCode = getText(deactivate,'code') processCode = getText(process,'code') #---------------------------------------------------------------------------- # CPP file #---------------------------------------------------------------------------- #---------------------------------------------------------------------------- # file header fcpp.write( '/****************************************************************\n' + 'XSPIF: cross(X) Standard PlugIn Framework: ' + 'XSPIF to AudioUnits\n' + T+filename_prefix+'.au.cpp'+'\n\n' + comment + ' This file is generated automatically from the file: '+xml_filename+'\n' + ' DO NOT EDIT BY HAND'+'\n' + T+'plugin ID: '+plugId+'\n' + T+'manufacturer ID: '+manufId+'\n' + T+'maker: '+maker+'\n' + T+'copyright: '+copyright+'\n' + ' ***************************************************************/\n' + '\n' ) #includes and independent code fcpp.write('\n' +'#include <AUEffectBase.h>'+'\n' +'\n' +'// XSPIF macros'+'\n' +'#define XSPIF_GET_SAMPLE_RATE() (GetSampleRate())'+'\n' +'#define XSPIF_GET_VECTOR_SIZE() (vector_size)'+'\n' +'#define XSPIF_CONTROLOUT() (// NO control out until macosx 10.3)'+'\n' +'\n' +'// <from '+xml_filename+'>'+'\n' +pluginCode+'\n' +'// </from '+xml_filename+'>'+'\n') #---------------------------------------------------------------------------- # Class declaration (TODO:change the base according to the purpose) fcpp.write('class '+pluginLabel+': public AUEffectBase'+'\n' +'{'+'\n' +'public:'+'\n' +T+pluginLabel+'(AudioUnit component);'+'\n' +T+'~'+pluginLabel+'();'+'\n' +T+''+'\n' +T+'virtual OSStatus ProcessBufferLists( AudioUnitRenderActionFlags & ioActionFlags,'+'\n' +T+' const AudioBufferList & inBuffer,'+'\n' +T+' AudioBufferList & outBuffer,'+'\n' +T+' UInt32 inFramesToProcess);'+'\n' +T+''+'\n' +T+'virtual UInt32 SupportedNumChannels(const AUChannelInfo** outInfo);'+'\n' +T+'virtual ComponentResult Initialize();'+'\n' +T+'virtual ComponentResult GetParameterInfo( AudioUnitScope inScope,'+'\n' +T+' AudioUnitParameterID inParameterId,'+'\n' +T+' AudioUnitParameterInfo &outParameterInfo);'+'\n' +T+''+'\n' +'private:'+'\n' ) # declare params if params != []: fcpp.write(T+'//params'+'\n' +T+'enum Parameters'+'\n' +T+'{'+'\n') for param in params: label = getText(param,'label') fcpp.write(T*2+'k'+label+','+'\n') fcpp.write(T+''+'\n' +T*2+'kNumParams'+'\n' +T+'};'+'\n' +'\n') for param in params: label = getText(param,'label') default = getText(param,'default') min = getText(param,'min') max = getText(param,'max') fcpp.write(T+'static const float '+label+'Default = '+default+';'+'\n' +T+'static const float '+label+'Min = '+min+';'+'\n' +T+'static const float '+label+'Max = '+max+';'+'\n' +T+'float '+label+';'+'\n' +'\n') # declare supported number of channels numInputs = numOutputs = 0 for pin in pins: dir = getText(pin,'dir') channels = int(str(getText(pin,'channels'))) if dir == 'In': numInputs += channels elif dir == 'Out': numOutputs += channels if numInputs < numOutputs: numchannels = numInputs else: numchannels = numOutputs numchannels = str(numchannels) numInputs = str(numInputs) numOutputs = str(numOutputs) fcpp.write(T+'enum {'+'\n' +T*2+'kNumSupportedNumChannels = '+numchannels+'\n' +T+'};'+'\n' +'\n' +T+'static AUChannelInfo m_aobSupportedNumChannels[kNumSupportedNumChannels];'+'\n' +'\n' ) # process declaration fcpp.write(T+'OSStatus Process(const AudioBufferList& obInBuffers,'+'\n' +T+' AudioBufferList& obOutBuffers,'+'\n' +T+' UInt32 inFramesToProcess,'+'\n' +T+' AudioUnitRenderActionFlags& ioactionFlags);'+'\n' +'\n' ) # states if states != []: fcpp.write(T+'// states'+'\n') for state in states: label = getText(state,'label') type = getText(state,'type') fcpp.write(T+type+' '+label+';'+'\n') fcpp.write('\n' +T+'long vector_size;'+'\n' +'};'+'\n' +'\n' ) #---------------------------------------------------------------------------- # CPP file #-------------------------------------------------------------------------- fcpp.write( """ //---------------------------------------------------------------------------- // IMPLEMENTATION //-------------------------------------------------------------------------- """) #-------------------------------------------------------------------------- fcpp.write('//------------------------------------------------------'+'\n' +'COMPONENT_ENTRY('+pluginLabel+')'+'\n' +'\n' ) #-------------------------------------------------------------------------- # Topology numInputs = numOutputs = 0 for pin in pins: dir = getText(pin,'dir') channels = int(str(getText(pin,'channels'))) if dir == 'In': numInputs += channels elif dir == 'Out': numOutputs += channels numInputs = str(numInputs) numOutputs = str(numOutputs) fcpp.write('#define NUM_INPUTS '+numInputs+'\n' +'#define NUM_OUTPUTS '+numOutputs+'\n' ) fcpp.write('//------------------------------------------------------'+'\n' +'AUChannelInfo '+pluginLabel+'::m_aobSupportedNumChannels['+pluginLabel+'::kNumSupportedNumChannels] = {{'+numInputs+','+numOutputs+'}};'+'\n' +'\n' ) #-------------------------------------------------------------------------- # Plugin Class # Plugin Constructor fcpp.write('//------------------------------------------------------'+'\n' +pluginLabel+'::'+pluginLabel+'(AudioUnit component): AUEffectBase(component)'+'\n' +'{'+'\n' +T+'CreateElements();'+'\n' +'\n') fcpp.write('\n' +T+'// instantiate callback'+'\n' +T+instantiateCode+'\n' +'\n') if params != []: for param in params: label = getText(param,'label') fcpp.write(T+'SetParameter(k'+label+','+label+'Default);'+'\n') fcpp.write('}'+'\n' +'\n') # Plugin Deconstructor fcpp.write('//------------------------------------------------------'+'\n' +pluginLabel+'::~'+pluginLabel+'()'+'\n' +'{'+'\n' ) fcpp.write('\n' +T+'// deinstantiate callback'+'\n' +T+deinstantiateCode+'\n' +'\n') fcpp.write('}'+'\n' +'\n') # Plugin infos fcpp.write('//------------------------------------------------------'+'\n' +'UInt32 '+pluginLabel+'::SupportedNumChannels(const AUChannelInfo **outInfo)'+'\n' +'{'+'\n' +T+'if(outInfo != NULL)'+'\n' +T*2+'*outInfo = &m_aobSupportedNumChannels[0];'+'\n' +T+'return kNumSupportedNumChannels;'+'\n' +'}'+'\n' +'\n' ) fcpp.write('//------------------------------------------------------'+'\n' +'ComponentResult '+pluginLabel+'::GetParameterInfo(AudioUnitScope inScope,'+'\n' +' AudioUnitParameterID inParameterID,'+'\n' +' AudioUnitParameterInfo &outParameterInfo)'+'\n' +'{'+'\n' +T+'if(inScope != kAudioUnitScope_Global)'+'\n' +T*2+'return kAudioUnitErr_InvalidParameter;'+'\n' +'\n' +T+'ComponentResult result = noErr;'+'\n' +'\n' +T+'outParameterInfo.flags = kAudioUnitParameterFlag_IsWritable | kAudioUnitParameterFlag_IsReadable | kAudioUnitParameterFlag_Global;'+'\n' +'\n' +T+'char *pcName = outParameterInfo.name;'+'\n' +'\n' +T+'switch(inParameterID)'+'\n' +T+'{'+'\n') if params != []: for param in params: label = getText(param,'label') caption = getText(param,'caption') fcpp.write(T*2+'case k'+label+':'+'\n' +T*3+'strcpy(pcName,"'+caption+'");'+'\n' +T*3+'outParameterInfo.unit = kAudioUnitParameterUnit_Generic;'+'\n' +T*3+'outParameterInfo.minValue = '+label+'Min;'+'\n' +T*3+'outParameterInfo.maxValue = '+label+'Max;'+'\n' +T*3+'outParameterInfo.defaultValue = '+label+'Default;'+'\n' +T*3+'break;'+'\n' +'\n' ) fcpp.write(T*2+'default:'+'\n' +T*3+'result = kAudioUnitErr_InvalidParameter;'+'\n' +T*3+'break;'+'\n' +T+'}'+'\n' +'\n' +T+'return result;'+'\n' +T+'}'+'\n' ) # Plugin suspend and resume fcpp.write('//------------------------------------------------------'+'\n' +'ComponentResult '+pluginLabel+'::Initialize()'+'\n' +'{'+'\n' +T+'return noErr;'+'\n' +'}'+'\n') #-------------------------------------------------------------------------- # processing fcpp.write('//------------------------------------------------------'+'\n' +'OSStatus '+pluginLabel+'::ProcessBufferLists(AudioUnitRenderActionFlags &ioActionFlags,'+'\n' +' const AudioBufferList &inBuffer,'+'\n' +' AudioBufferList &outBuffer,'+'\n' +' UInt32 inFramesToProcess)'+'\n' +'{'+'\n' +T+'ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;'+'\n' +'\n' +T+'UInt32 uiInBuffers = inBuffer.mNumberBuffers;'+'\n' +T+'UInt32 uiOutBuffers = outBuffer.mNumberBuffers;'+'\n' +'\n' +T+'if(uiInBuffers != NUM_INPUTS || uiOutBuffers != NUM_OUTPUTS)'+'\n' +T*2+'return kAudioUnitErr_FormatNotSupported;'+'\n' +'\n' +T+'return Process(inBuffer, outBuffer, inFramesToProcess, ioActionFlags);'+'\n' +'}'+'\n' +'\n') fcpp.write('#define XSPIF_WRITE_SAMPLE(dest,index,source) ((dest)[(index)] = (source))'+'\n' +'\n') fcpp.write('//------------------------------------------------------'+'\n' +'OSStatus '+pluginLabel+'::Process(const AudioBufferList &obInBuffers,'+'\n' +' AudioBufferList &obOutBuffers,'+'\n' +' UInt32 inFramesToProcess,'+'\n' +' AudioUnitRenderActionFlags &ioActionFlags)'+'\n' +'{'+'\n' +T+'ioActionFlags &= ~kAudioUnitRenderAction_OutputIsSilence;'+'\n' +'\n') for i in range(0,int(numInputs)): fcpp.write(T+'const AudioBuffer &obInBuffer'+str(i)+' = obInBuffers.mBuffers['+str(i)+'];'+'\n') for i in range(0,int(numOutputs)): fcpp.write(T+'AudioBuffer &obOutBuffer'+str(i)+' = obOutBuffers.mBuffers['+str(i)+'];'+'\n') i=0 for pin in pins: dir = getText(pin,'dir') if dir == 'In': i = i+1 label = getText(pin,'label') channels = int(str(getText(pin,'channels'))) for j in range(0,channels): fcpp.write(T+'const Float32 *'+label+str(i+j)+' = (const Float32 *)obInBuffer'+str(i+j-1)+'.mData;'+'\n') i=0 for pin in pins: dir = getText(pin,'dir') if dir == 'Out': i = i+1 label = getText(pin,'label') channels = int(str(getText(pin,'channels'))) for j in range(0,channels): fcpp.write(T+'Float32 *'+label+str(i+j)+' = (Float32 *)obOutBuffer'+str(i+j-1)+'.mData;'+'\n') fcpp.write(T+'vector_size = inFramesToProcess;'+'\n' +'\n' ) if params != []: fcpp.write(T+'//retrieving params'+'\n') for param in params: label = getText(param,'label') fcpp.write(T+label+' = GetParameter(k'+label+');'+'\n') fcpp.write(T+'//updating states'+'\n') for param in params: paramCode = getText(param,'code') fcpp.write(T+paramCode+'\n') fcpp.write(T+'//****************************************'+'\n' +processCode+'\n' +'//****************************************'+'\n' +'\n' +T+'return noErr;'+'\n' +'}'+'\n' ) fcpp.close print T+'done' #-------------------------------------------------------------------------- # r fr = file(filename_prefix + '.au.r','w') print T+'writing : '+filename_prefix+'.au.r' fr.write('#include <AudioUnit/AudioUnit.r>'+'\n' +'\n' +'// Note that resource IDs must be spaced 2 apart for the '+"'STR'"+' name and description'+'\n' +'#define kAudioUnitResID_'+pluginLabel+' 10000'+'\n' +'\n' +'// So you need to define these appropriately for your audio unit.'+'\n' +"// For the name the convention is to provide your company name and end it with a ':',"+'\n' +'// then provide the name of the AudioUnit.'+'\n' +'// The Description can be whatever you want.'+'\n' +'// For an effect unit the Type and SubType should be left the way they are defined here...'+'\n' +'//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'+'\n' +'// SampleEffectUnit'+'\n' +'#define RES_ID kAudioUnitResID_'+pluginLabel+'\n' +'#define COMP_TYPE '+"'aufx'"+'\n' +'#define COMP_SUBTYPE '+plugId+'\n' +'#define COMP_MANUF '+manufId+'\n' +'#define VERSION 0x00010000'+'\n' +'#define NAME "'+maker+': '+pluginCaption+'"'+'\n' +'#define DESCRIPTION "'+pluginCaption+'"'+'\n' +'#define ENTRY_POINT "'+pluginLabel+'Entry"'+'\n' +'\n' +'#include "AUResources.r"'+'\n' ) fr.close print T+'done' fexp = file(filename_prefix + '.exp','w') print T+'writing : '+filename_prefix+'.exp' fexp.write('_'+pluginLabel+'Entry') fexp.close print T+'done' return
def write(domTree, filenamePrefix): """ This method is called by xspif2ladspa.py It writes the LADSPA plugin's source file [filenamePrefix].ladspa.c for the linux platform with the help of the DOM tree [domTree]. """ #create output file(s) xml_filename = (filenamePrefix + '.xspif') c_filename = (filenamePrefix + '.ladspa.c') fc = file(c_filename, 'w') # Get the domTree direct childs pluginId = getText(domTree, 'plugId') pluginLabel = getText(domTree, 'label') pluginManufId = getText(domTree, "manufId") pluginCaption = getText(domTree, 'caption') pluginComments = getText(domTree, 'comment') pluginMaker = getText(domTree, 'maker') pluginCopyright = getText(domTree, 'copyright') pluginCode = getText(domTree, 'code') pins = domTree.getElementsByTagName('pin') params = domTree.getElementsByTagName('param') states = domTree.getElementsByTagName('state') controlouts = domTree.getElementsByTagName('controlout') callbacks = domTree.getElementsByTagName('callback') # Get the callbacks by their name instantiate = None deinstantiate = None process = None processEvents = None activate = None deactivate = None if callbacks == []: print T + "Warning: you haven't implemented any callback" + '\n' else: for callback in callbacks: text = str(getText(callback, 'label')) if text == 'instantiate': instantiate = callback elif text == 'deinstantiate': deinstantiate = callback elif text == 'activate': activate = callback elif text == 'deactivate': deactivate = callback elif text == 'process': process = callback elif text == 'processEvents': print 'XSPIF Warning: callback "processEvents" not implemented for LADSPA for now' #processEvents = callback else: print('Warning: callback ' + text + ' is not known from ladspa.py' + '\n') # useful variables... separator = '\n' + '/****************************************************************/' + '\n' #---------------------------------------------------------------------------- # make lists with ports # make a list with pins audio_ports = [] for pin_node in pins: pin_label = getText(pin_node, 'label') pin_channels = int(getText(pin_node, 'channels')) pin_dir = getText(pin_node, 'dir') pin_caption = getText(pin_node, 'caption') for i in range(1, pin_channels + 1): audio_ports.append([pin_label + str(i), pin_dir, pin_caption]) # make a list with params param_ports = [] for p_node in params: p_label = getText(p_node, 'label') p_caption = getText(p_node, 'caption') p_type = getText(p_node, 'type') param_ports.append([p_label, 'param', p_caption, p_type]) # make a list with controlouts controlout_ports = [] for c_node in controlouts: c_label = getText(c_node, 'label') c_caption = getText(c_node, 'caption') c_type = getText(c_node, 'type') controlout_ports.append([c_label, 'controlout', c_caption, c_type]) # make a list with all ports ports = audio_ports + param_ports + controlout_ports #---------------------------------------------------------------------------- # file header fc.write( '/****************************************************************' + '\n' + 'XSPIF: cross(X) Standard PlugIn Framework: ' + 'XSPIF to LADSPA' + '\n' + T + c_filename + '\n' + '\n' + pluginComments + ' This file is generated automatically from the file: ' + xml_filename + '\n' + T + 'plugin ID: ' + pluginId + '\n' + T + 'manufacturer ID: ' + pluginManufId + '\n' + T + 'maker: ' + pluginMaker + '\n' + T + 'copyright: ' + pluginCopyright + '\n' + '****************************************************************/' + '\n' + '\n' + '\n' + '\n') #---------------------------------------------------------------------------- # includes and independant code fc.write('#include <stdlib.h>' + '\n' + '#include <string.h>' + '\n' + '#include "ladspa.h"' + '\n' + '\n') # macros fc.write('\n' + '\n' + separator) fc.write('// Macro for getting the sample rate' + '\n') fc.write('#undef XSPIF_GET_SAMPLE_RATE' + '\n' '#define XSPIF_GET_SAMPLE_RATE() (plugin_data->sample_rate)' + '\n') fc.write('// Macro for getting the vector_size' + '\n') fc.write('#undef XSPIF_GET_VECTOR_SIZE' + '\n' '#define XSPIF_GET_VECTOR_SIZE() (sample_count)' + '\n') if ([] != controlouts): fc.write('// Macro for control output' + '\n') fc.write('#undef XSPIF_CONTROLOUT' + '\n' '#define XSPIF_CONTROLOUT(dest, index, source)' + '(*(dest) = (LADSPA_DATA(source)))' + '\n') fc.write('\n' + '\n' + separator) fc.write(pluginCode + '\n') #---------------------------------------------------------------------------- # Port declaration # write ports declaration in the c file fc.write('\n' + '\n' + separator) fc.write('/* Audio and parameters ports */' + '\n') for port in ports: fc.write('#define PORT_' + port[0].upper() + T * 2 + str(ports.index(port)) + '\n') #--------------------------------------------------- # LADSPA DESCRIPTOR #--------------------------------------------------- fc.write('\n' + '\n' + separator) fc.write('static LADSPA_Descriptor *' + pluginLabel.lower() + 'Descriptor = NULL;' + '\n') #--------------------------------------------------- # The plugin structure contains ... #--------------------------------------------------- fc.write('\n' + '\n' + separator) fc.write('// The plugin structure ' + '\n') fc.write('typedef struct {' + '\n' + '\n') # the sample rate fc.write(T + 'LADSPA_Data sample_rate;' + '\n') # ...the pointers for the ports ... fc.write(T + '// Pointers to the ports:\n') for port in ports: fc.write(T + 'LADSPA_Data *m_pf' + port[0] + ';' + '\n') fc.write('\n') # ...a copy of the parameter and its last value for local use... fc.write(T + '// Internal copy of the parameters and its last value:\n') for port in param_ports: fc.write(T + port[3] + ' ' + port[0] + ';' + '\n') fc.write(T + port[3] + ' _last_' + port[0] + ';' + '\n') fc.write('\n') # ... and the states ...(except the code_states declared as global) fc.write(T + '// Internal states:\n') if ([] != states): for state_node in states: state_label = getText(state_node, 'label') state_type = getText(state_node, 'type') fc.write(T + state_type + ' ' + state_label + ';' + '\n') # run adding gain fc.write(T + 'LADSPA_Data run_adding_gain;' + '\n') fc.write('}' + pluginLabel + ';' + '\n') #---------------------------------------------------------------------------- # Plugin descriptor # TODO: handle case with multiple plugs in a dll with a 'for' loop #---------------------------------------------------------------------------- fc.write('\n' + '\n' + separator) fc.write( 'const LADSPA_Descriptor *ladspa_descriptor(unsigned long index) {' + '\n' * 2 + T + 'switch (index) {' + '\n' + T + 'case 0:' + '\n' + T * 2 + 'return ' + pluginLabel.lower() + 'Descriptor;' + '\n' + T + 'default:' + '\n' + T * 2 + 'return NULL;' + '\n' + T + '}' + '\n' + '}' + '\n') #--------------------------------------------------------------------------- # Plugin activate function #--------------------------------------------------------------------------- if (activate): activate_code = getText(activate, 'code') fc.write('\n' + '\n' + separator) fc.write('// Initialise and activate a plugin instance.' + '\n') fc.write('static void activate' + pluginLabel + '(LADSPA_Handle instance) {' + '\n' * 2 + T + pluginLabel + ' *plugin_data = (' + pluginLabel + '*)instance;' + '\n') writeGetStates(fc, states) writeGetParams(fc, params) writeCallbackCode(fc, activate) writeUpdateStates(fc, states) fc.write('\n' + '}' + '\n') #--------------------------------------------------------------------------- # Plugin deactivate function #--------------------------------------------------------------------------- if (deactivate): deactivate_code = getText(deactivate, 'code') fc.write('\n' + '\n' + separator) fc.write('// Deactivate a plugin instance.' + '\n') fc.write('static void deactivate' + pluginLabel + '(LADSPA_Handle instance) {' + '\n' * 2 + T + pluginLabel + ' *plugin_data = (' + pluginLabel + '*)instance;' + '\n') writeGetStates(fc, states) writeGetParams(fc, params) writeCallbackCode(fc, deactivate) writeUpdateStates(fc, states) fc.write('\n' + '}' + '\n') #---------------------------------------------------------------------------- # Plugin cleanup function (=deinstantiate) #---------------------------------------------------------------------------- fc.write('\n' + '\n' + separator) fc.write('/* Cleanup is the "de-instantiate" function.' + '\n' + 'Free the memory allocated in "instantiate" in this function */' + '\n') fc.write('static void cleanup' + pluginLabel + '(LADSPA_Handle instance) {' + '\n' * 2 + T + pluginLabel + '* plugin_data = (' + pluginLabel + '*)instance;' + '\n') # If deinstantiate is implemented in the meta-plugin # Get the states, so that any structure can be deleted fom memory: if (deinstantiate): writeGetStates(fc, states) writeGetParams(fc, params) writeCallbackCode(fc, deinstantiate) fc.write(T + 'free(instance);' + '\n' + '}' + '\n') #---------------------------------------------------------------------------- # Plugin connect function #---------------------------------------------------------------------------- fc.write('\n' + '\n' + separator) head = 'static void connectPort' + pluginLabel + '(' fc.write(head + ' LADSPA_Handle instance,' + '\n' + ' ' * len(head) + ' unsigned long port,' + '\n' + ' ' * len(head) + ' LADSPA_Data *data) {' + '\n' * 2 + T + pluginLabel + ' *plugin;' + '\n' + '\n' + T + 'plugin = (' + pluginLabel + ' *)instance;' + '\n' + T + 'switch (port) {' + '\n') for port in ports: fc.write(T + 'case PORT_' + port[0].upper() + ':' + '\n' + T * 2 + 'plugin->m_pf' + port[0] + ' = data;' + '\n' + T * 2 + 'break;' + '\n') fc.write(T + '}' + '\n' + '}' + '\n') #---------------------------------------------------------------------------- # Instantiate function #---------------------------------------------------------------------------- head = 'static LADSPA_Handle instantiate' + pluginLabel + '(' fc.write('\n' + '\n' + separator) fc.write(head + ' const LADSPA_Descriptor *descriptor,' + '\n' + ' ' * len(head) + ' unsigned long s_rate) {' + '\n' * 2 + T + pluginLabel + ' *plugin_data = (' + pluginLabel + ' *)malloc(sizeof(' + pluginLabel + '));' + '\n' * 2) if (instantiate): fc.write(T + '// States:' + '\n') if ([] != states): for state_node in states: state_label = getText(state_node, 'label') state_type = getText(state_node, 'type') fc.write(T + state_type + ' ' + state_label + ';' + '\n') # fc.write(T + 'if (plugin_data) {'+ '\n') fc.write(T * 2 + 'plugin_data->sample_rate =(LADSPA_Data)s_rate;' + '\n') if (instantiate): writeCallbackCode(fc, instantiate) writeUpdateStates(fc, states) fc.write('\n' + T + 'return (LADSPA_Handle)plugin_data;' + '\n' + '}' + '\n') #---------------------------------------------------------------------------- # Define once the XSPIF_WRITE_SAMPLE macro for 'run-replacing' process #---------------------------------------------------------------------------- fc.write('\n' + '\n' + separator) fc.write('// Macro for run-replacing processing' + '\n') fc.write( '#undef XSPIF_WRITE_SAMPLE' + '\n' '#define XSPIF_WRITE_SAMPLE(dest, index, source) ((dest)[(index)] = (source))' + '\n') #---------------------------------------------------------------------------- # Run (process) function #---------------------------------------------------------------------------- head = 'static void run' + pluginLabel + '(' fc.write('\n' + '\n' + separator) fc.write(head + 'LADSPA_Handle instance,' + '\n' + ' ' * len(head) + 'unsigned long sample_count) {' + '\n' * 2 + T + pluginLabel + ' *plugin_data = (' + pluginLabel + '*)instance;' + '\n' + '\n') # get the parameters new and last values for param_node in params: param_caption = getText(param_node, 'caption') param_label = getText(param_node, 'label') param_type = getText(param_node, 'type') fc.write(T + '/*' + param_caption + '*/' + '\n' + T + 'const LADSPA_Data ' + param_label + ' = *(plugin_data->m_pf' + param_label + ');' + '\n') fc.write(T + param_type + ' _last_' + param_label + ' = plugin_data->_last_' + param_label + ';' + '\n') fc.write('\n') for port in audio_ports: if port[1] == 'In': fc.write(T + '/* Audio input: ' + port[0] + '*/' + '\n') fc.write(T + 'const LADSPA_Data * const ' + port[0]) elif port[1] == 'Out': fc.write(T + '/* Audio output: ' + port[0] + '*/' + '\n') fc.write(T + 'LADSPA_Data * const ' + port[0]) fc.write(' = plugin_data->m_pf' + port[0] + ';' + '\n' + '\n') for port in controlout_ports: fc.write(T + '/* Control output: ' + port[1] + '*/' + '\n') fc.write(T + 'LADSPA_Data * ' + port[0] + ' = plugin_data->m_pf' + port[0] + ';' + '\n' + '\n') # ...the states... writeGetStates(fc, states) # check if param changed and perform necessary conversions if any: fc.write('\n') fc.write( T + '// Check if param changed and perform necessary conversions if any:' + '\n') for param_node in params: param_label = getText(param_node, 'label') fc.write(T + 'if (' + param_label + ' != _last_' + param_label + ') {' + '\n') param_code = getText(param_node, 'code') if ('' != param_code): fc.write(param_code + '\n') fc.write(T * 2 + 'plugin_data->_last_' + param_label + ' = ' + param_label + ';' + '\n' + T + '}' + '\n') fc.write('\n') # here is the DSP algorithm writeCallbackCode(fc, process) # Update the states in the plugin structure writeUpdateStates(fc, states) fc.write('}' + '\n') #---------------------------------------------------------------------------- # Re- define then the XSPIF_WRITE_SAMPLE macro for 'run-adding' process #---------------------------------------------------------------------------- fc.write('\n' + '\n' + separator) fc.write('// Macro for run-adding processing' + '\n') fc.write( '#undef XSPIF_WRITE_SAMPLE' + '\n' '#define XSPIF_WRITE_SAMPLE(dest, index, source) ((dest)[(index)] += (source))' + '\n') #---------------------------------------------------------------------------- # setRunAddinGain function #---------------------------------------------------------------------------- fc.write('\n' + '\n' + separator) fc.write('static void setRunAddingGain' + pluginLabel + '(LADSPA_Handle instance,' + '\n' + T * 3 + 'LADSPA_Data gain) {' + '\n' * 2 + T + '((' + pluginLabel + '*)instance)->run_adding_gain = gain;' + '\n' + '}' + '\n') #---------------------------------------------------------------------------- # Run_adding (process with accumulation) function #---------------------------------------------------------------------------- fc.write('\n' + '\n' + separator) fc.write('static void runAdding' + pluginLabel + '(LADSPA_Handle instance, unsigned long sample_count) {' + '\n' * 2 + T + pluginLabel + ' *plugin_data = (' + pluginLabel + '*)instance;' + '\n' + T + 'LADSPA_Data run_adding_gain = plugin_data->run_adding_gain;' + '\n' + '\n') # get the parameters new and last values for param_node in params: param_caption = getText(param_node, 'caption') param_label = getText(param_node, 'label') param_type = getText(param_node, 'type') fc.write(T + '/*' + param_caption + '*/' + '\n' + T + 'const LADSPA_Data ' + param_label + ' = *(plugin_data->m_pf' + param_label + ');' + '\n') fc.write(T + param_type + ' _last_' + param_label + ' = plugin_data->_last_' + param_label + ';' + '\n') fc.write('\n') for port in audio_ports: fc.write(T + '/* Audio input: ' + port[0] + '*/' + '\n') if port[1] == 'In': fc.write(T + 'const LADSPA_Data * const ' + port[0]) elif port[1] == 'Out': fc.write(T + 'LADSPA_Data * const ' + port[0]) fc.write(' = plugin_data->m_pf' + port[0] + ';' + '\n' + '\n') for port in controlout_ports: fc.write(T + '/* Control output: ' + port[1] + '*/' + '\n') fc.write(T + 'LADSPA_Data * ' + port[0] + ' = plugin_data->m_pf' + port[0] + ';' + '\n' + '\n') # ...the states... writeGetStates(fc, states) # check if param changed and perform necessary conversions if any: fc.write('\n') fc.write( T + '// Check if param changed and perform necessary conversions if any:' + '\n') for param_node in params: param_label = getText(param_node, 'label') fc.write(T + 'if (' + param_label + ' != _last_' + param_label + ') {' + '\n') param_code = getText(param_node, 'code') if ('' != param_code): fc.write(param_code + '\n') fc.write(T * 2 + 'plugin_data->_last_' + param_label + ' = ' + param_label + ';' + '\n' + T + '}' + '\n') fc.write('\n') # here is the DSP algorithm writeCallbackCode(fc, process) # Update the states in the plugin structure writeUpdateStates(fc, states) fc.write('}' + '\n') #---------------------------------------------------------------------------- # Init function #---------------------------------------------------------------------------- # define shortcuts pD = pluginLabel.lower() + 'Descriptor' fc.write('\n' + '\n' + separator) fc.write( '/* _init() is called automatically when the plugin library is first loaded. */' + '\n') fc.write('void _init() {' + '\n' * 2 + T + 'char **port_names;' + '\n' + T + 'LADSPA_PortDescriptor *port_descriptors;' + '\n' + T + 'LADSPA_PortRangeHint *port_range_hints;' + '\n' + '\n' + T + pD + ' = ' + '\n' + T + ' (LADSPA_Descriptor *)malloc(sizeof(LADSPA_Descriptor));' + '\n' + '\n' + T + 'if (' + pD + ') {' + '\n' + T * 2 + pD + '->UniqueID = ' + pluginId + ';' + '\n' + T * 2 + pD + '->Label = strdup("' + pluginLabel + '");' + '\n' + T * 2 + pD + '->Name = strdup("' + pluginCaption + '");' + '\n' + T * 2 + pD + '->Maker = strdup("' + pluginMaker + '");' + '\n' + T * 2 + pD + '->Copyright = strdup("' + pluginCopyright + '");' + '\n' + T * 2 + pD + '->PortCount = ' + str(len(ports)) + ';' + '\n' + '\n' + T * 2 + 'port_descriptors = (LADSPA_PortDescriptor *)calloc(' + str(len(ports)) + ',' + '\n' + T * 2 + ' sizeof(LADSPA_PortDescriptor));' + '\n' + T * 2 + pD + '->PortDescriptors =' + '\n' + T * 2 + '(const LADSPA_PortDescriptor *)port_descriptors;' + '\n' + '\n' + T * 2 + 'port_range_hints = (LADSPA_PortRangeHint *)calloc(' + str(len(ports)) + ',' + '\n' + T * 2 + ' sizeof(LADSPA_PortRangeHint));' + '\n' + T * 2 + pD + '->PortRangeHints =' + '\n' + T * 2 + ' (const LADSPA_PortRangeHint *)port_range_hints;' + '\n' + '\n' + T * 2 + 'port_names = (char **)calloc(' + str(len(ports)) + ', sizeof(char*));' + '\n' + T * 2 + pD + '->PortNames =' + '\n' + T * 2 + ' (const char **)port_names;' + '\n' + '\n') # Write descriptors for input parameters (TODO: ouput parameters in xml) control_nodes = params + controlouts for cp_node in control_nodes: pl = 'PORT_' + getText(cp_node, 'label').upper() # shortcut fc.write(T * 2 + '/* Parameters for ' + getText(cp_node, 'caption') + ' */' + '\n' + T * 2 + 'port_descriptors[' + pl + '] =' + '\n') if ('param' == cp_node.nodeName): fc.write(T * 2 + ' LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;' + '\n') elif ('controlout' == cp_node.nodeName): fc.write(T * 2 + ' LADSPA_PORT_OUTPUT | LADSPA_PORT_CONTROL;' + '\n') fc.write(T * 2 + 'port_names[' + pl + '] =' + '\n' + T * 2 + ' strdup("' + getText(cp_node, 'caption') + '");' + '\n' + T * 2 + 'port_range_hints[' + pl + '].HintDescriptor =' + '\n' + T * 2) cp_hint = 0 # for these ' | ' sake!! cp_min = getText(cp_node, 'min').replace('f', '', 1) cp_max = getText(cp_node, 'max').replace('f', '', 1) cp_default = getText(cp_node, 'default').replace('f', '', 1) if ('' != cp_min): fc.write(' LADSPA_HINT_BOUNDED_BELOW') cp_hint = 1 if ('' != cp_max): if (1 == cp_hint): fc.write(' | LADSPA_HINT_BOUNDED_ABOVE') else: fc.write(' LADSPA_HINT_BOUNDED_ABOVE') cp_hint = 1 if ('' != cp_default): # TODO: add default_low and default_high if float(cp_default) <= float(cp_min): if (1 == cp_hint): fc.write(' | LADSPA_HINT_DEFAULT_MINIMUM') else: fc.write(' LADSPA_HINT_DEFAULT_MINIMUM') cp_hint = 1 elif float(cp_default) >= float(cp_max): if (1 == cp_hint): fc.write(' | LADSPA_HINT_DEFAULT_MAXIMUM') else: fc.write(' LADSPA_HINT_DEFAULT_MAXIMUM') cp_hint = 1 else: # if default is neither 'min' nor 'max', let it be middle range if (1 == cp_hint): fc.write(' | LADSPA_HINT_DEFAULT_MIDDLE') else: fc.write(' LADSPA_HINT_DEFAULT_MIDDLE') else: if (1 == cp_hint): fc.write(' | LADSPA_HINT_DEFAULT_NONE') else: fc.write(' LADSPA_HINT_DEFAULT_NONE') cp_hint = 1 if ('log' == getText(cp_node, 'mapping')): if (1 == cp_hint): fc.write(' | LADSPA_HINT_LOGARITHMIC') else: fc.write(' LADSPA_HINT_LOGARITHMIC') cp_hint = 1 if ('int' == getText(cp_node, 'type')): if (1 == cp_hint): fc.write(' | LADSPA_HINT_INTEGER') else: fc.write(' LADSPA_HINT_INTEGER') cp_hint = 1 fc.write(';' + '\n') if ('' != cp_min): fc.write(T * 2 + 'port_range_hints[' + pl + '].LowerBound = ' + cp_min + ';' + '\n') if ('' != cp_max): fc.write(T * 2 + 'port_range_hints[' + pl + '].UpperBound = ' + cp_max + ';' + '\n') fc.write('\n' * 2) # Write descriptors for audio pins for port in audio_ports: fc.write(T * 2 + '/* Parameters for PORT_' + port[0] + ' */' + '\n' + T * 2 + 'port_descriptors[PORT_' + port[0].upper() + '] =' + '\n') if 'In' == port[1]: fc.write(T * 2 + ' LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO;' + '\n') elif 'Out' == port[1]: fc.write(T * 2 + ' LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO;' + '\n') fc.write(T * 2 + 'port_names[PORT_' + port[0].upper() + '] =' + '\n' + T * 2 + ' strdup("' + port[2] + '");' + '\n' + T * 2 + 'port_range_hints[PORT_' + port[0].upper() + '].HintDescriptor = 0;' + '\n' + '\n') # Declare the methods if (instantiate): fc.write(T * 2 + pD + '->instantiate = instantiate' + pluginLabel + ';' + '\n') else: fc.write(T * 2 + pD + '->instantiate = NULL;' + '\n') if (deinstantiate): fc.write(T * 2 + pD + '->cleanup = cleanup' + pluginLabel + ';' + '\n') else: fc.write(T * 2 + pD + '->cleanup = NULL;' + '\n') if (activate): fc.write(T * 2 + pD + '->activate = activate' + pluginLabel + ';' + '\n') else: fc.write(T * 2 + pD + '->activate = NULL;' + '\n') if (deactivate): fc.write(T * 2 + pD + '->deactivate = deactivate' + pluginLabel + ';' + '\n') else: fc.write(T * 2 + pD + '->deactivate = NULL;' + '\n') fc.write(T * 2 + pD + '->connect_port = connectPort' + pluginLabel + ';' + '\n' + T * 2 + pD + '->run = run' + pluginLabel + ';' + '\n' + T * 2 + pD + '->run_adding = runAdding' + pluginLabel + ';' + '\n' + T * 2 + pD + '->set_run_adding_gain = setRunAddingGain' + pluginLabel + ';' + '\n') fc.write(T + '}' + '\n' + '}' + '\n') #---------------------------------------------------------------------------- # fini #---------------------------------------------------------------------------- fc.write('\n' + '\n' + separator) fc.write( '/* _fini() is called automatically when the library is unloaded. */' + '\n') fc.write('void _fini() {' + '\n' * 2 + ' \t' * 1 + 'int i;' + '\n' + ' \t' * 1 + 'if (' + pD + ') {' + '\n' + ' \t' * 2 + 'free((char *)' + pD + '->Label);' + '\n' + ' \t' * 2 + 'free((char *)' + pD + '->Name);' + '\n' + ' \t' * 2 + 'free((char *)' + pD + '->Maker);' + '\n' + ' \t' * 2 + 'free((char *)' + pD + '->Copyright);' + '\n' + ' \t' * 2 + 'free((LADSPA_PortDescriptor *)' + pD + '->PortDescriptors);' + '\n' + ' \t' * 2 + 'for (i = 0; i < ' + pD + '->PortCount; i++)' + '\n' + ' \t' * 3 + 'free((char *)(' + pD + '->PortNames[i]));' + '\n' + ' \t' * 2 + 'free((char **)' + pD + '->PortNames);' + '\n' + ' \t' * 2 + 'free((LADSPA_PortRangeHint *)' + pD + '->PortRangeHints);' + '\n' + ' \t' * 2 + 'free(' + pD + ');' + '\n' + ' \t' * 1 + '}' + '\n') fc.write('}' + '\n') #---------------------------------------------------------------------------- # close file fc.close print T + 'done' #--------------------------------------------------------------------------- # MAKFILE #--------------------------------------------------------------------------- print T + 'writing : Makefile' fm = file('Makefile', 'w') fm.write(''' ############################################################################### # XSPIF: cross(X) Standard PlugIn Framework: # This makefile is generated automatically by the module "ladspa.py" # and from the file: ''' + xml_filename + ''' # ############################################################################### # INSTALLATION DIRECTORIES # # Change these if you want to install somewhere else. In particularly # you may wish to remove the middle "local/" part of each entry. INSTALL_PLUGINS_DIR = /usr/local/lib/ladspa/ INSTALL_INCLUDE_DIR = /usr/include/ #INSTALL_BINARY_DIR = /usr/local/bin/ ############################################################################### # # GENERAL # NAME = ''' + filenamePrefix + ''' INCLUDES = -I$(LADSPA_PATH) -I. -I.. LIBRARIES = -ldl -lm CFLAGS = $(INCLUDES) -DPD -O3 -Wall -funroll-loops -fPIC -Wno-unused -Wno-parentheses -Wno-switch CXXFLAGS = $(CFLAGS) SRC = $(NAME).ladspa.c OBJ = $(NAME).ladspa.o PLUGIN = $(NAME).ladspa.so CC = cc CPP = c++ ############################################################################### # # RULES TO BUILD PLUGINS FROM C OR C++ CODE # $(PLUGIN): $(OBJ) $(LD) -o $(PLUGIN) $(OBJ) -shared $(OBJ): $(SRC) $(CC) $(CFLAGS) -o $(OBJ) -c $(SRC) # For now, all LADSPA plugins generated with XSPIF are in C # $(PLUGIN): $(SRC) $(OBJ) ladspa.h # $(CPP) $(CXXFLAGS) -o $*.o -c $*.cpp # $(CPP) -o $*.ladspa.so $*.ladspa.o -shared ############################################################################### # # TARGETS # install: $(PLUGIN) cp $(PLUGIN) $(LADSPA_PATH) targets: $(PLUGIN) ############################################################################### # # UTILITIES # clean: -rm -f `find . -name "*.o"` -rm -f `find .. -name "*~"` -rm -f `find .. -name "core*"` ############################################################################### ''') fm.close print T + 'done' return
def write(plugin,filename_prefix): print T+'writing : '+filename_prefix+'.vst.cpp' fcpp = file(filename_prefix + '.vst.cpp','w') xml_filename = filename_prefix + ".xspif" ######## independent code here ############ # Variables plugId = getText(plugin,'plugId') manufId = getText(plugin,'manufId') maker = getText(plugin,'maker') pluginLabel = getText(plugin,'label') # forces the first letter to upper case becaus it will be used to name the C++ class pluginLabel.title(); copyright = getText(plugin,'copyright') comment = getText(plugin,'comment')+'\n' pluginCode = getText(plugin,'code')+'\n' pluginCaption = getText(plugin,'caption') params = plugin.getElementsByTagName('param') controlouts = plugin.getElementsByTagName('controlout') states = plugin.getElementsByTagName("state") pins = plugin.getElementsByTagName("pin") callbacks = plugin.getElementsByTagName('callback') instantiate = None deinstantiate = None process = None processEvents = None activate = None deactivate = None if callbacks == [] : print T+ "Warning: you haven't implemented any callback"+'\n' else : for callback in callbacks: text = str(getText(callback,'label')) if text == 'instantiate': instantiate = callback if text == 'deinstantiate': deinstantiate = callback if text == 'activate': activate = callback if text == 'deactivate': deactivate = callback if text == 'process': process = callback if text == 'processEvents': print 'XSPIF Warning: callback "processEvents" not implemented in VST for now' #processEvents = callback instantiateCode = getText(instantiate,'code') deinstantiateCode = getText(deinstantiate,'code') activateCode = getText(activate,'code') deactivateCode = getText(deactivate,'code') processCode = getText(process,'code') #---------------------------------------------------------------------------- # CPP file #---------------------------------------------------------------------------- #---------------------------------------------------------------------------- # file header fcpp.write( '/****************************************************************\n' + 'XSPIF: cross(X) Standard PlugIn Framework: ' + 'XSPIF to VST\n' + T+filename_prefix+'.vst.cpp'+'\n\n' + comment + ' This file is generated automatically from the file: '+xml_filename+'\n' + ' DO NOT EDIT BY HAND'+'\n' + T+'plugin ID: '+plugId+'\n' + T+'manufacturer ID: '+manufId+'\n' + T+'maker: '+maker+'\n' + T+'copyright: '+copyright+'\n' + ' ***************************************************************/\n' + '\n' ) #---------------------------------------------------------------------------- # includes and independant code fcpp.write('#include "audioeffectx.h"\n' +'#include <string.h>\n' +'\n' +'#define XSPIF_GET_SAMPLE_RATE() (sr)'+'\n' +'#define XSPIF_GET_VECTOR_SIZE() (vector_size)'+'\n' +'#define XSPIF_CONTROLOUT(label, index, value) (controlOut(label,index,value))'+'\n' +'\n') fcpp.write('// from '+xml_filename+'\n' +pluginCode) #---------------------------------------------------------------------------- # parameters constants and declaration ... if params != []: fcpp.write('\n' +'//-------------------------------------------------------------\n' +'// Parameters constant and declarations\n' +'\n' +'const double e = exp(1.0);'+'\n') for param in params: label = getText(param,'label') caption = getText(param,'caption') comment = getText(param,'comment') default = getText(param,'default') min = getText(param,'min') max = getText(param,'max') mapping = getText(param,'mapping') fcpp.write('\n' +'// '+caption+'\n' +'// '+comment+'\n' +'const float '+label+'Default'+' = '+default+';\n' +'const float '+label+'Min'+' = '+min+';\n' +'const float '+label+'Max'+' = '+max+';\n' +'\n') # write params enumeration fcpp.write('enum\n' +'{\n') for param in params: label = getText(param,'label') fcpp.write(T+'k'+label+',\n') fcpp.write('\n' +T+'kNumParams'+'\n' +'};\n' +'\n') #---------------------------------------------------------------------------- # controlouts if controlouts != []: fcpp.write('\n' +'//-------------------------------------------------------------\n' +'// control out ranges constant and declarations\n' +'\n') for controlout in controlouts: label = getText(controlout,'label') caption = getText(controlout,'caption') comment = getText(controlout,'comment') min = getText(controlout,'min') max = getText(controlout,'max') fcpp.write('\n' +'// '+caption+'\n' +'// '+comment+'\n' +'const float '+label+'Min'+' = '+min+';\n' +'const float '+label+'Max'+' = '+max+';\n' +'\n') # write controlouts enumeration fcpp.write('enum\n' +'{\n') for controlout in controlouts: label = getText(controlout,'label') fcpp.write(T+label+',\n') fcpp.write('\n' +T+'kNumControlouts'+'\n' +'};\n' +'\n') #---------------------------------------------------------------------------- # classes fcpp.write('\n' +'class '+pluginLabel+';\n' +'\n') # PluginProgram classdeclaration fcpp.write('//-------------------------------------------------------------\n' +'//'+pluginLabel+'Program'+'\n' +'\n' +'class '+pluginLabel+'Program'+'\n' +'{'+'\n' +'friend class '+pluginLabel+';'+'\n' +'public:'+'\n' +T+pluginLabel+'Program();'+'\n' +T+'~'+pluginLabel+'Program() {}'+'\n' +'private:'+'\n') if params != []: for param in params: label = getText(param,'label') fcpp.write(T+'float f'+label+'; // normalized param'+'\n') fcpp.write(T+'char name[24];'+'\n' +'};'+'\n' +'\n') # Plugin classdeclaration fcpp.write( """ //------------------------------------------------------------- //"""+pluginLabel+""" class """+pluginLabel+""" : public AudioEffectX { public: """+pluginLabel+"""(audioMasterCallback audioMaster); ~"""+pluginLabel+"""(); virtual void process(float **inputs, float **outputs, long sampleframes); virtual void processReplacing(float **inputs, float **outputs, long sampleFrames); virtual void setProgram(long program); virtual void setProgramName(char *name); virtual void getProgramName(char *name); virtual void setParameter(long index, float value); virtual float getParameter(long index); virtual void getParameterDisplay(long index, char *text); virtual void getParameterName(long index, char *text); virtual void getParameterLabel(long index, char *label); virtual bool getEffectName(char *name); virtual bool getVendorString(char *text); virtual void resume(); virtual long canDo(char *text); virtual void suspend(); private: """+pluginLabel+"""Program *programs; """) if controlouts != []: fcpp.write('void '+pluginLabel+'::controlout(long label, long index, float value)'+'\n') if params != []: fcpp.write(T+'// <parameters>'+'\n') for param in params: label = getText(param,'label') type = getText(param,'type') default = label+'Default' min = label+'Min' max = label+'Max' fcpp.write(T+type+' '+label+'; // param'+'\n' +T+'float f'+label+'; // normalized param'+'\n' +'\n') fcpp.write(T+'// </parameters>'+'\n' +'\n') # macros fcpp.write(T+'float sr;'+'\n' +T+'long vector_size;'+'\n' +'\n') # states if states != []: fcpp.write(T+'// states'+'\n') for state in states: label = getText(state,'label') type = getText(state,'type') fcpp.write(T+type+' '+label+';'+'\n') fcpp.write('};'+'\n' +'\n') fcpp.write(""" //---------------------------------------------------------------------------- // IMPLEMENTATION //---------------------------------------------------------------------------- """) #---------------------------------------------------------------------------- # includes fcpp.write('#include <stdio.h>\n' +'#include <string.h>\n') #---------------------------------------------------------------------------- # Topology numInputs = numOutputs = 0 for pin in pins: dir = getText(pin,'dir') channels = int(str(getText(pin,'channels'))) if dir == 'In': numInputs += channels elif dir == 'Out': numOutputs += channels numInputs = str(numInputs) numOutputs = str(numOutputs) fcpp.write('//-------------------------------------------------------------'+'\n' +'// Config'+'\n' +'#define NUM_INPUTS '+numInputs+'\n' +'#define NUM_OUTPUTS '+numOutputs+'\n' +'#define NUM_PRESETS '+'4'+'\n' +'//infos'+'\n' +'#define ID '+plugId+'\n' +'#define NAME "'+pluginCaption+'"'+'\n' +'#define VENDOR "'+maker+'"'+'\n' +'\n' +'#define CAN_PROCESS_REPLACING true'+'\n' +'#define CAN_MONO true'+'\n' +'\n' +'\n' ) #---------------------------------------------------------------------------- # Presets class fcpp.write('//-------------------------------------------------------------'+'\n' +'// '+pluginLabel+'Program class implementation'+'\n' +'\n' +pluginLabel+'Program::'+pluginLabel+'Program()'+'\n' +'{'+'\n') if params != [] : for param in params: label = getText(param,'label') default = label+'Default' min = label+'Min' max = label+'Max' mapping = getText(param,'mapping') type = getText(param,'type') if mapping == 'lin': fcpp.write(T+'f'+label+' = ('+default+'-'+min+')/('+max+'-'+min+');'+'\n') elif mapping == 'log': # external->internal is logarithmic # ( log(v)-log(min) ) / ( log(max)-log(min) ) # = ( log(v/min) / ( log(max/min) ) fcpp.write(T+'f'+label+' = log('+default+'/'+min+')/(log('+max+'/'+min+'));'+'\n') fcpp.write(T+'\n' +T+'strcpy(name,"'+pluginCaption+'");'+'\n' +'}'+'\n' +'\n' ) #---------------------------------------------------------------------------- # Plugin Class #---------------------------------------------------------------------------- # Plugin Constructor fcpp.write('//-------------------------------------------------------------'+'\n' +'// '+pluginLabel+' class implementation'+'\n' +'\n' +pluginLabel+'::'+pluginLabel+'(audioMasterCallback audioMaster)'+'\n' +': AudioEffectX(audioMaster, NUM_PRESETS, kNumParams)'+'\n' +'{'+'\n' +T+'programs = new '+pluginLabel+'Program[numPrograms];'+'\n' +T+'\n' +T+'sr = getSampleRate();'+'\n' +T+'if(sr<0.f) sr = 44100.f;'+'\n' +'\n' ) if params != []: for param in params: label = getText(param,"label") default = label+'Default' min = label+'Min' max = label+'Max' mapping = getText(param,'mapping') if mapping == 'lin': fcpp.write(T+'f'+label+' = ('+default+'-'+min+')/('+max+'-'+min+');'+'\n' +T+label+' = '+default+';'+'\n') elif mapping == 'log': # external->internal is logarithmic # ( log(v)-log(min) ) / ( log(max)-log(min) ) # = ( log(v/min) / ( log(max/min) ) fcpp.write(T+'f'+label+' = log('+default+'/'+min+')/(log('+max+'/'+min+'));'+'\n') fcpp.write('\n' +T+'// instantiate callback'+'\n' +T+instantiateCode+'\n' +'\n') fcpp.write( """ if(programs) setProgram(0); setUniqueID(ID); setNumInputs(NUM_INPUTS); setNumOutputs(NUM_OUTPUTS); canProcessReplacing(CAN_PROCESS_REPLACING); canMono(CAN_MONO); } """) #---------------------------------------------------------------------------- # Plugin Destructor fcpp.write('//-------------------------------------------------------------'+'\n' +pluginLabel+'::~'+pluginLabel+'()'+'\n' +'{'+'\n' +T+'if(programs)'+'\n' +T*2+'delete[] programs;'+'\n') fcpp.write('\n' +T+'// deinstantiate callback'+'\n' +T+deinstantiateCode+'\n' +'\n') fcpp.write('}'+'\n' +'\n') #---------------------------------------------------------------------------- # Plugin infos if controlouts != []: fcpp.write('//-------------------------------------------------------------'+'\n' +'// control out'+'\n' +'\n' +'void '+pluginLabel+'::controlout(long label, long index, float value)'+'\n' +'{'+'\n' +T+'char out=0;'+'\n' +T+'char cc=0;'+'\n' +T+'switch(label)'+'\n' +T+'{'+'\n') i = 38 # first MIDI CC that will be used fo control out for controlout in controlouts: label = getText(controlout,'label') max = label+'Max' min = label+'Min' mapping = getText(controlout,'mapping') controloutCode = getText(param,'code') if i >127: print 'warning too much controlouts CC numbers exceed 127' return fcpp.write(T*2+'case '+label+' :'+'\n' +T*3+'cc = '+str(i)+';'+'\n') i = i+1 if mapping == 'lin': fcpp.write(T*3+'out = 127*((value-'+min+')/('+max+'-'+min+'));'+'\n') elif mapping == 'log': # fcpp.write(T*3+'out = 127*(log(value/'+min+')/log('+max+'/'+min+'));'+'\n') fcpp.write( '\n' +T*2+'default: return;'+'\n' +T+'}'+'\n' +'\n' +T+'if(index>vector_size)'+'\n' +T*2+'index = vector_size;'+'\n' +'\n' +T+'VstMidiEvent vstEvent;'+'\n' +T+'VstEvents vstEvents; '+'\n' +T+'vstEvents.numEvents =1; '+'\n' +T+'vstEvents.reserved = 0; '+'\n' +T+'vstEvents.events[0] = (VstEvent*)(&vstEvent); '+'\n' +T+'vstEvents.events[1]= NULL; '+'\n' +'\n' +T+'memset(&vstEvent, 0, sizeof(vstEvent));'+'\n' +T+'vstEvent.type = kVstMidiType;'+'\n' +T+'vstEvent.byteSize = 24;'+'\n' +T+'vstEvent.deltaFrames = index; '+'\n' +T+'vstEvent.midiData[0]=0xb0; // tells it sends midi CC'+'\n' +T+'vstEvent.midiData[1]=cc; '+'\n' +T+'vstEvent.midiData[2]=out; '+'\n' +T+'((AudioEffectX *)this)->sendVstEventsToHost(&vstEvents);'+'\n' +'}'+'\n' ) #---------------------------------------------------------------------------- # Plugin infos fcpp.write('//-------------------------------------------------------------'+'\n' +'// '+pluginLabel+' information'+'\n' +'\n' +'bool '+pluginLabel+'::getEffectName(char *name) {'+'\n' +T+'strcpy(name, NAME); // name max 32 char'+'\n' +T+'return true;}'+'\n' +'\n' +'bool '+pluginLabel+'::getVendorString(char *text) {'+'\n' +T+'strcpy(text, VENDOR); // vendor max 64 char'+'\n' +T+'return true;}'+'\n' +'\n') fcpp.write('long '+pluginLabel+'::canDo(char* text)'+'\n' +'\t'+'{'+'\n' ) if controlouts != []: fcpp.write( T+'if (!strcmp (text, "sendVstMidiEvent")) return 1;'+'\n' +T+'if (!strcmp (text, "sendVstEvents")) return 1;'+'\n') fcpp.write( T+'//if (!strcmp (text, "receiveVstEvents")) return 1;'+'\n' +T+'//if (!strcmp (text, "receiveVstMidiEvent")) return 1;'+'\n' +T+"return 0; // explicitly can't do; 0 => don't know"+'\n' +'}'+'\n' ) #---------------------------------------------------------------------------- # Plugin suspend and resume fcpp.write('//-------------------------------------------------------------'+'\n' +'void '+pluginLabel+'::resume()'+'\n' +'{'+'\n') if controlouts != []: fcpp.write(T+'wantEvents();'+'\n') fcpp.write(T+activateCode+'\n' +'}'+'\n' +'\n' +'void '+pluginLabel+'::suspend()'+'\n' +'{'+'\n' +T+deactivateCode+'\n' +'}'+'\n' +'\n') #---------------------------------------------------------------------------- # Params handling # setProgram fcpp.write('//-------------------------------------------------------------'+'\n' +'void '+pluginLabel+'::setProgram(long program)'+'\n' +'{'+'\n' +T+pluginLabel+'Program *ap = &programs[program];'+'\n' +'\n' +T+'curProgram = program;'+'\n') if params != []: for param in params: label = getText(param,"label") fcpp.write(T+'setParameter(k'+label+', ap->f'+label+');'+'\n') fcpp.write('}'+'\n' +'\n') #setProgramName fcpp.write('//-------------------------------------------------------------'+'\n' +'void '+pluginLabel+'::setProgramName(char *name)'+'\n' +'{'+'\n' +T+'strcpy(programs[curProgram].name, name);'+'\n' +'}'+'\n' +'\n') #getProgramName fcpp.write('//-------------------------------------------------------------'+'\n' +'void '+pluginLabel+'::getProgramName(char *name)'+'\n' +'{'+'\n' +T+'strcpy(name, programs[curProgram].name);'+'\n' +'}'+'\n' +'\n') #setParameter fcpp.write('//-------------------------------------------------------------'+'\n' +'void '+pluginLabel+'::setParameter(long index, float value)'+'\n' +'{'+'\n' +T+pluginLabel+'Program *ap = &programs[curProgram];'+'\n' +'\n' +T+'switch(index)'+'\n' +T+'{'+'\n') if params != []: for param in params: label = getText(param,'label') max = label+'Max' min = label+'Min' mapping = getText(param,'mapping') type = getText(param,'type') paramCode = getText(param,'code') fcpp.write(T*2+'case k'+label+' :'+'\n' +T*3+'f'+label+' = ap->f'+label+' = value;'+'\n') if mapping == 'lin': fcpp.write(T*3+label+' = ('+type+')(value*('+max+'-'+min+')+'+min+');'+'\n') elif mapping == 'log': # normalized = (log(param/min)) / (log(max/min)) # => log(param/min) = normalized * log(max/min) # => param = min * (max/min)^normalized fcpp.write(T*3+label+' = ('+type+')'+min+'*pow('+max+'/'+min+',f'+label+');'+'\n') fcpp.write(paramCode +T*2+'break;'+'\n' +'\n') fcpp.write(T*3+'default: break;'+'\n' +T+'}'+'\n' +'}'+'\n' +'\n') #getParameter fcpp.write('//-------------------------------------------------------------'+'\n' +'float '+pluginLabel+'::getParameter(long index)'+'\n' +'{'+'\n' +T+'float v = 0.f;'+'\n' +'\n' +T+'switch(index)'+'\n' +T+'{'+'\n') if params != []: for param in params: label = getText(param,'label') fcpp.write(T*2+'case k'+label+' : v = f'+label+'; break;'+'\n') fcpp.write(T*2+'default: break;'+'\n' +T+'}'+'\n' +T+'return v;'+'\n' +'}'+'\n' +'\n') #getParameterName fcpp.write('//-------------------------------------------------------------'+'\n' +'void '+pluginLabel+'::getParameterName(long index, char *label)'+'\n' +'{'+'\n' +T+'switch(index)'+'\n' +T+'{'+'\n') if params != []: for param in params: label = getText(param,'label') caption = getText(param,'caption') fcpp.write(T*2+'case k'+label+' : strcpy(label, "'+caption+'"); break;'+'\n') fcpp.write(T*2+'default: break;'+'\n' +T+'}'+'\n' +'}'+'\n' +'\n') #getParameterDisplay fcpp.write('//-------------------------------------------------------------'+'\n' +'void '+pluginLabel+'::getParameterDisplay(long index, char *text)'+'\n' +'{'+'\n' +T+'switch(index)'+'\n' +T+'{'+'\n') if params != []: for param in params: label = getText(param,'label') type = getText(param,'type') if type == 'float': fcpp.write(T*2+'case k'+label+' : float2string('+label+', text); break;'+'\n') elif type == 'int': fcpp.write(T*2+'case k'+label+' : long2string('+label+', text); break;'+'\n') fcpp.write(T*2+'default: break;'+'\n' +T+'}'+'\n' +'}'+'\n' +'\n') #getParameterLabel fcpp.write('//-------------------------------------------------------------'+'\n' +'void '+pluginLabel+'::getParameterLabel(long index, char *label)'+'\n' +'{'+'\n' +T+'switch(index)'+'\n' +T+'{'+'\n') if params != []: for param in params: label = getText(param,'label') unit = getText(param,'unit') fcpp.write(T*2+'case k'+label+' : strcpy(label, "'+unit+'"); break;'+'\n') fcpp.write(T*2+'default: break;'+'\n' +T+'}'+'\n' +'}'+'\n' +'\n') #---------------------------------------------------------------------------- # processing # process fcpp.write('#define XSPIF_WRITE_SAMPLE(dest,index,source) ((dest)[(index)] += (source))'+'\n' +'\n' +'//-------------------------------------------------------------'+'\n' +'void '+pluginLabel+'::process(float **inputs, float **outputs, long sampleframes)'+'\n' +'{'+'\n') i=0 for pin in pins: dir = getText(pin,'dir') if dir == 'In': label = getText(pin,'label') channels = int(str(getText(pin,'channels'))) for j in range(0,channels): i = i+1 fcpp.write(T+'float *'+label+str(j+1)+' = inputs['+str(i-1)+'];'+'\n') i=0 for pin in pins: dir = getText(pin,'dir') if dir == 'Out': label = getText(pin,'label') channels = int(str(getText(pin,'channels'))) for j in range(0,channels): i = i + 1 fcpp.write(T+'float *'+label+str(j+1)+' = outputs['+str(i-1)+'];'+'\n') fcpp.write(T+'vector_size = sampleframes;'+'\n' +'\n' +processCode+'\n' +'}'+'\n' +'\n' +'#undef XSPIF_WRITE_SAMPLE'+'\n' +'\n') # processReplacing fcpp.write('#define XSPIF_WRITE_SAMPLE(dest,index,source) ((dest)[(index)] = (source))'+'\n' +'\n' +'//-------------------------------------------------------------'+'\n' +'void '+pluginLabel+'::processReplacing(float **inputs, float **outputs, long sampleframes)'+'\n' +'{'+'\n') i=0 for pin in pins: dir = getText(pin,'dir') if dir == 'In': label = getText(pin,'label') channels = int(str(getText(pin,'channels'))) for j in range(0,channels): i = i+1 fcpp.write(T+'float *'+label+str(j+1)+' = inputs['+str(i-1)+'];'+'\n') i=0 for pin in pins: dir = getText(pin,'dir') if dir == 'Out': label = getText(pin,'label') channels = int(str(getText(pin,'channels'))) for j in range(0,channels): i = i+1 fcpp.write(T+'float *'+label+str(j+1)+' = outputs['+str(i-1)+'];'+'\n') fcpp.write(T+'vector_size = sampleframes;'+'\n' +'\n' +processCode+'\n' +'}'+'\n' +'\n' +'#undef XSPIF_WRITE_SAMPLE'+'\n' +'\n') #---------------------------------------------------------------------------- # main entry point fcpp.write( ''' //------------------------------------------------------------- // main entry point //------------------------------------------------------------- bool oome = false; #if MAC #pragma export on #endif // prototype of the export function main #if BEOS #define main main_plugin extern "C" __declspec(dllexport) AEffect *main_plugin (audioMasterCallback audioMaster); #elif MACX #define main main_macho extern "C" AEffect *main_macho (audioMasterCallback audioMaster); #else AEffect *main (audioMasterCallback audioMaster); #endif AEffect *main (audioMasterCallback audioMaster) { // get vst version if (!audioMaster (0, audioMasterVersion, 0, 0, 0, 0)) return 0; // old version AudioEffect* effect = new '''+pluginLabel+'''(audioMaster); if (!effect) return 0; if (oome) { delete effect; return 0; } return effect->getAeffect (); } #if MAC #pragma export off #endif #if WIN32 #include <windows.h> void* hInstance; BOOL WINAPI DllMain (HINSTANCE hInst, DWORD dwReason, LPVOID lpvReserved) { hInstance = hInst; return 1; } #endif ''') fcpp.close print T+'done' # def file for windows: exports the plugins entry point print T+'writing : '+filename_prefix+'.vst.def' fdef = file(filename_prefix + '.vst.def','w') fdef.write('LIBRARY '+pluginLabel+'\n' +"DESCRIPTION '"+pluginCaption+"'"+'\n' +'EXPORTS main'+'\n') fdef.close print T+'done' return
def write(plugin, filename_prefix): print T + 'writing : ' + filename_prefix + '.vst.cpp' fcpp = file(filename_prefix + '.vst.cpp', 'w') xml_filename = filename_prefix + ".xspif" ######## independent code here ############ # Variables plugId = getText(plugin, 'plugId') manufId = getText(plugin, 'manufId') maker = getText(plugin, 'maker') pluginLabel = getText(plugin, 'label') # forces the first letter to upper case becaus it will be used to name the C++ class pluginLabel.title() copyright = getText(plugin, 'copyright') comment = getText(plugin, 'comment') + '\n' pluginCode = getText(plugin, 'code') + '\n' pluginCaption = getText(plugin, 'caption') params = plugin.getElementsByTagName('param') controlouts = plugin.getElementsByTagName('controlout') states = plugin.getElementsByTagName("state") pins = plugin.getElementsByTagName("pin") callbacks = plugin.getElementsByTagName('callback') instantiate = None deinstantiate = None process = None processEvents = None activate = None deactivate = None if callbacks == []: print T + "Warning: you haven't implemented any callback" + '\n' else: for callback in callbacks: text = str(getText(callback, 'label')) if text == 'instantiate': instantiate = callback if text == 'deinstantiate': deinstantiate = callback if text == 'activate': activate = callback if text == 'deactivate': deactivate = callback if text == 'process': process = callback if text == 'processEvents': print 'XSPIF Warning: callback "processEvents" not implemented in VST for now' #processEvents = callback instantiateCode = getText(instantiate, 'code') deinstantiateCode = getText(deinstantiate, 'code') activateCode = getText(activate, 'code') deactivateCode = getText(deactivate, 'code') processCode = getText(process, 'code') #---------------------------------------------------------------------------- # CPP file #---------------------------------------------------------------------------- #---------------------------------------------------------------------------- # file header fcpp.write( '/****************************************************************\n' + 'XSPIF: cross(X) Standard PlugIn Framework: ' + 'XSPIF to VST\n' + T + filename_prefix + '.vst.cpp' + '\n\n' + comment + ' This file is generated automatically from the file: ' + xml_filename + '\n' + ' DO NOT EDIT BY HAND' + '\n' + T + 'plugin ID: ' + plugId + '\n' + T + 'manufacturer ID: ' + manufId + '\n' + T + 'maker: ' + maker + '\n' + T + 'copyright: ' + copyright + '\n' + ' ***************************************************************/\n' + '\n') #---------------------------------------------------------------------------- # includes and independant code fcpp.write( '#include "audioeffectx.h"\n' + '#include <string.h>\n' + '\n' + '#define XSPIF_GET_SAMPLE_RATE() (sr)' + '\n' + '#define XSPIF_GET_VECTOR_SIZE() (vector_size)' + '\n' + '#define XSPIF_CONTROLOUT(label, index, value) (controlOut(label,index,value))' + '\n' + '\n') fcpp.write('// from ' + xml_filename + '\n' + pluginCode) #---------------------------------------------------------------------------- # parameters constants and declaration ... if params != []: fcpp.write( '\n' + '//-------------------------------------------------------------\n' + '// Parameters constant and declarations\n' + '\n' + 'const double e = exp(1.0);' + '\n') for param in params: label = getText(param, 'label') caption = getText(param, 'caption') comment = getText(param, 'comment') default = getText(param, 'default') min = getText(param, 'min') max = getText(param, 'max') mapping = getText(param, 'mapping') fcpp.write('\n' + '// ' + caption + '\n' + '// ' + comment + '\n' + 'const float ' + label + 'Default' + ' = ' + default + ';\n' + 'const float ' + label + 'Min' + ' = ' + min + ';\n' + 'const float ' + label + 'Max' + ' = ' + max + ';\n' + '\n') # write params enumeration fcpp.write('enum\n' + '{\n') for param in params: label = getText(param, 'label') fcpp.write(T + 'k' + label + ',\n') fcpp.write('\n' + T + 'kNumParams' + '\n' + '};\n' + '\n') #---------------------------------------------------------------------------- # controlouts if controlouts != []: fcpp.write( '\n' + '//-------------------------------------------------------------\n' + '// control out ranges constant and declarations\n' + '\n') for controlout in controlouts: label = getText(controlout, 'label') caption = getText(controlout, 'caption') comment = getText(controlout, 'comment') min = getText(controlout, 'min') max = getText(controlout, 'max') fcpp.write('\n' + '// ' + caption + '\n' + '// ' + comment + '\n' + 'const float ' + label + 'Min' + ' = ' + min + ';\n' + 'const float ' + label + 'Max' + ' = ' + max + ';\n' + '\n') # write controlouts enumeration fcpp.write('enum\n' + '{\n') for controlout in controlouts: label = getText(controlout, 'label') fcpp.write(T + label + ',\n') fcpp.write('\n' + T + 'kNumControlouts' + '\n' + '};\n' + '\n') #---------------------------------------------------------------------------- # classes fcpp.write('\n' + 'class ' + pluginLabel + ';\n' + '\n') # PluginProgram classdeclaration fcpp.write( '//-------------------------------------------------------------\n' + '//' + pluginLabel + 'Program' + '\n' + '\n' + 'class ' + pluginLabel + 'Program' + '\n' + '{' + '\n' + 'friend class ' + pluginLabel + ';' + '\n' + 'public:' + '\n' + T + pluginLabel + 'Program();' + '\n' + T + '~' + pluginLabel + 'Program() {}' + '\n' + 'private:' + '\n') if params != []: for param in params: label = getText(param, 'label') fcpp.write(T + 'float f' + label + '; // normalized param' + '\n') fcpp.write(T + 'char name[24];' + '\n' + '};' + '\n' + '\n') # Plugin classdeclaration fcpp.write(""" //------------------------------------------------------------- //""" + pluginLabel + """ class """ + pluginLabel + """ : public AudioEffectX { public: """ + pluginLabel + """(audioMasterCallback audioMaster); ~""" + pluginLabel + """(); virtual void process(float **inputs, float **outputs, long sampleframes); virtual void processReplacing(float **inputs, float **outputs, long sampleFrames); virtual void setProgram(long program); virtual void setProgramName(char *name); virtual void getProgramName(char *name); virtual void setParameter(long index, float value); virtual float getParameter(long index); virtual void getParameterDisplay(long index, char *text); virtual void getParameterName(long index, char *text); virtual void getParameterLabel(long index, char *label); virtual bool getEffectName(char *name); virtual bool getVendorString(char *text); virtual void resume(); virtual long canDo(char *text); virtual void suspend(); private: """ + pluginLabel + """Program *programs; """) if controlouts != []: fcpp.write('void ' + pluginLabel + '::controlout(long label, long index, float value)' + '\n') if params != []: fcpp.write(T + '// <parameters>' + '\n') for param in params: label = getText(param, 'label') type = getText(param, 'type') default = label + 'Default' min = label + 'Min' max = label + 'Max' fcpp.write(T + type + ' ' + label + '; // param' + '\n' + T + 'float f' + label + '; // normalized param' + '\n' + '\n') fcpp.write(T + '// </parameters>' + '\n' + '\n') # macros fcpp.write(T + 'float sr;' + '\n' + T + 'long vector_size;' + '\n' + '\n') # states if states != []: fcpp.write(T + '// states' + '\n') for state in states: label = getText(state, 'label') type = getText(state, 'type') fcpp.write(T + type + ' ' + label + ';' + '\n') fcpp.write('};' + '\n' + '\n') fcpp.write(""" //---------------------------------------------------------------------------- // IMPLEMENTATION //---------------------------------------------------------------------------- """) #---------------------------------------------------------------------------- # includes fcpp.write('#include <stdio.h>\n' + '#include <string.h>\n') #---------------------------------------------------------------------------- # Topology numInputs = numOutputs = 0 for pin in pins: dir = getText(pin, 'dir') channels = int(str(getText(pin, 'channels'))) if dir == 'In': numInputs += channels elif dir == 'Out': numOutputs += channels numInputs = str(numInputs) numOutputs = str(numOutputs) fcpp.write( '//-------------------------------------------------------------' + '\n' + '// Config' + '\n' + '#define NUM_INPUTS ' + numInputs + '\n' + '#define NUM_OUTPUTS ' + numOutputs + '\n' + '#define NUM_PRESETS ' + '4' + '\n' + '//infos' + '\n' + '#define ID ' + plugId + '\n' + '#define NAME "' + pluginCaption + '"' + '\n' + '#define VENDOR "' + maker + '"' + '\n' + '\n' + '#define CAN_PROCESS_REPLACING true' + '\n' + '#define CAN_MONO true' + '\n' + '\n' + '\n') #---------------------------------------------------------------------------- # Presets class fcpp.write( '//-------------------------------------------------------------' + '\n' + '// ' + pluginLabel + 'Program class implementation' + '\n' + '\n' + pluginLabel + 'Program::' + pluginLabel + 'Program()' + '\n' + '{' + '\n') if params != []: for param in params: label = getText(param, 'label') default = label + 'Default' min = label + 'Min' max = label + 'Max' mapping = getText(param, 'mapping') type = getText(param, 'type') if mapping == 'lin': fcpp.write(T + 'f' + label + ' = (' + default + '-' + min + ')/(' + max + '-' + min + ');' + '\n') elif mapping == 'log': # external->internal is logarithmic # ( log(v)-log(min) ) / ( log(max)-log(min) ) # = ( log(v/min) / ( log(max/min) ) fcpp.write(T + 'f' + label + ' = log(' + default + '/' + min + ')/(log(' + max + '/' + min + '));' + '\n') fcpp.write(T + '\n' + T + 'strcpy(name,"' + pluginCaption + '");' + '\n' + '}' + '\n' + '\n') #---------------------------------------------------------------------------- # Plugin Class #---------------------------------------------------------------------------- # Plugin Constructor fcpp.write( '//-------------------------------------------------------------' + '\n' + '// ' + pluginLabel + ' class implementation' + '\n' + '\n' + pluginLabel + '::' + pluginLabel + '(audioMasterCallback audioMaster)' + '\n' + ': AudioEffectX(audioMaster, NUM_PRESETS, kNumParams)' + '\n' + '{' + '\n' + T + 'programs = new ' + pluginLabel + 'Program[numPrograms];' + '\n' + T + '\n' + T + 'sr = getSampleRate();' + '\n' + T + 'if(sr<0.f) sr = 44100.f;' + '\n' + '\n') if params != []: for param in params: label = getText(param, "label") default = label + 'Default' min = label + 'Min' max = label + 'Max' mapping = getText(param, 'mapping') if mapping == 'lin': fcpp.write(T + 'f' + label + ' = (' + default + '-' + min + ')/(' + max + '-' + min + ');' + '\n' + T + label + ' = ' + default + ';' + '\n') elif mapping == 'log': # external->internal is logarithmic # ( log(v)-log(min) ) / ( log(max)-log(min) ) # = ( log(v/min) / ( log(max/min) ) fcpp.write(T + 'f' + label + ' = log(' + default + '/' + min + ')/(log(' + max + '/' + min + '));' + '\n') fcpp.write('\n' + T + '// instantiate callback' + '\n' + T + instantiateCode + '\n' + '\n') fcpp.write(""" if(programs) setProgram(0); setUniqueID(ID); setNumInputs(NUM_INPUTS); setNumOutputs(NUM_OUTPUTS); canProcessReplacing(CAN_PROCESS_REPLACING); canMono(CAN_MONO); } """) #---------------------------------------------------------------------------- # Plugin Destructor fcpp.write( '//-------------------------------------------------------------' + '\n' + pluginLabel + '::~' + pluginLabel + '()' + '\n' + '{' + '\n' + T + 'if(programs)' + '\n' + T * 2 + 'delete[] programs;' + '\n') fcpp.write('\n' + T + '// deinstantiate callback' + '\n' + T + deinstantiateCode + '\n' + '\n') fcpp.write('}' + '\n' + '\n') #---------------------------------------------------------------------------- # Plugin infos if controlouts != []: fcpp.write( '//-------------------------------------------------------------' + '\n' + '// control out' + '\n' + '\n' + 'void ' + pluginLabel + '::controlout(long label, long index, float value)' + '\n' + '{' + '\n' + T + 'char out=0;' + '\n' + T + 'char cc=0;' + '\n' + T + 'switch(label)' + '\n' + T + '{' + '\n') i = 38 # first MIDI CC that will be used fo control out for controlout in controlouts: label = getText(controlout, 'label') max = label + 'Max' min = label + 'Min' mapping = getText(controlout, 'mapping') controloutCode = getText(param, 'code') if i > 127: print 'warning too much controlouts CC numbers exceed 127' return fcpp.write(T * 2 + 'case ' + label + ' :' + '\n' + T * 3 + 'cc = ' + str(i) + ';' + '\n') i = i + 1 if mapping == 'lin': fcpp.write(T * 3 + 'out = 127*((value-' + min + ')/(' + max + '-' + min + '));' + '\n') elif mapping == 'log': # fcpp.write(T * 3 + 'out = 127*(log(value/' + min + ')/log(' + max + '/' + min + '));' + '\n') fcpp.write('\n' + T * 2 + 'default: return;' + '\n' + T + '}' + '\n' + '\n' + T + 'if(index>vector_size)' + '\n' + T * 2 + 'index = vector_size;' + '\n' + '\n' + T + 'VstMidiEvent vstEvent;' + '\n' + T + 'VstEvents vstEvents; ' + '\n' + T + 'vstEvents.numEvents =1; ' + '\n' + T + 'vstEvents.reserved = 0; ' + '\n' + T + 'vstEvents.events[0] = (VstEvent*)(&vstEvent); ' + '\n' + T + 'vstEvents.events[1]= NULL; ' + '\n' + '\n' + T + 'memset(&vstEvent, 0, sizeof(vstEvent));' + '\n' + T + 'vstEvent.type = kVstMidiType;' + '\n' + T + 'vstEvent.byteSize = 24;' + '\n' + T + 'vstEvent.deltaFrames = index; ' + '\n' + T + 'vstEvent.midiData[0]=0xb0; // tells it sends midi CC' + '\n' + T + 'vstEvent.midiData[1]=cc; ' + '\n' + T + 'vstEvent.midiData[2]=out; ' + '\n' + T + '((AudioEffectX *)this)->sendVstEventsToHost(&vstEvents);' + '\n' + '}' + '\n') #---------------------------------------------------------------------------- # Plugin infos fcpp.write( '//-------------------------------------------------------------' + '\n' + '// ' + pluginLabel + ' information' + '\n' + '\n' + 'bool ' + pluginLabel + '::getEffectName(char *name) {' + '\n' + T + 'strcpy(name, NAME); // name max 32 char' + '\n' + T + 'return true;}' + '\n' + '\n' + 'bool ' + pluginLabel + '::getVendorString(char *text) {' + '\n' + T + 'strcpy(text, VENDOR); // vendor max 64 char' + '\n' + T + 'return true;}' + '\n' + '\n') fcpp.write('long ' + pluginLabel + '::canDo(char* text)' + '\n' + '\t' + '{' + '\n') if controlouts != []: fcpp.write(T + 'if (!strcmp (text, "sendVstMidiEvent")) return 1;' + '\n' + T + 'if (!strcmp (text, "sendVstEvents")) return 1;' + '\n') fcpp.write(T + '//if (!strcmp (text, "receiveVstEvents")) return 1;' + '\n' + T + '//if (!strcmp (text, "receiveVstMidiEvent")) return 1;' + '\n' + T + "return 0; // explicitly can't do; 0 => don't know" + '\n' + '}' + '\n') #---------------------------------------------------------------------------- # Plugin suspend and resume fcpp.write( '//-------------------------------------------------------------' + '\n' + 'void ' + pluginLabel + '::resume()' + '\n' + '{' + '\n') if controlouts != []: fcpp.write(T + 'wantEvents();' + '\n') fcpp.write(T + activateCode + '\n' + '}' + '\n' + '\n' + 'void ' + pluginLabel + '::suspend()' + '\n' + '{' + '\n' + T + deactivateCode + '\n' + '}' + '\n' + '\n') #---------------------------------------------------------------------------- # Params handling # setProgram fcpp.write( '//-------------------------------------------------------------' + '\n' + 'void ' + pluginLabel + '::setProgram(long program)' + '\n' + '{' + '\n' + T + pluginLabel + 'Program *ap = &programs[program];' + '\n' + '\n' + T + 'curProgram = program;' + '\n') if params != []: for param in params: label = getText(param, "label") fcpp.write(T + 'setParameter(k' + label + ', ap->f' + label + ');' + '\n') fcpp.write('}' + '\n' + '\n') #setProgramName fcpp.write( '//-------------------------------------------------------------' + '\n' + 'void ' + pluginLabel + '::setProgramName(char *name)' + '\n' + '{' + '\n' + T + 'strcpy(programs[curProgram].name, name);' + '\n' + '}' + '\n' + '\n') #getProgramName fcpp.write( '//-------------------------------------------------------------' + '\n' + 'void ' + pluginLabel + '::getProgramName(char *name)' + '\n' + '{' + '\n' + T + 'strcpy(name, programs[curProgram].name);' + '\n' + '}' + '\n' + '\n') #setParameter fcpp.write( '//-------------------------------------------------------------' + '\n' + 'void ' + pluginLabel + '::setParameter(long index, float value)' + '\n' + '{' + '\n' + T + pluginLabel + 'Program *ap = &programs[curProgram];' + '\n' + '\n' + T + 'switch(index)' + '\n' + T + '{' + '\n') if params != []: for param in params: label = getText(param, 'label') max = label + 'Max' min = label + 'Min' mapping = getText(param, 'mapping') type = getText(param, 'type') paramCode = getText(param, 'code') fcpp.write(T * 2 + 'case k' + label + ' :' + '\n' + T * 3 + 'f' + label + ' = ap->f' + label + ' = value;' + '\n') if mapping == 'lin': fcpp.write(T * 3 + label + ' = (' + type + ')(value*(' + max + '-' + min + ')+' + min + ');' + '\n') elif mapping == 'log': # normalized = (log(param/min)) / (log(max/min)) # => log(param/min) = normalized * log(max/min) # => param = min * (max/min)^normalized fcpp.write(T * 3 + label + ' = (' + type + ')' + min + '*pow(' + max + '/' + min + ',f' + label + ');' + '\n') fcpp.write(paramCode + T * 2 + 'break;' + '\n' + '\n') fcpp.write(T * 3 + 'default: break;' + '\n' + T + '}' + '\n' + '}' + '\n' + '\n') #getParameter fcpp.write( '//-------------------------------------------------------------' + '\n' + 'float ' + pluginLabel + '::getParameter(long index)' + '\n' + '{' + '\n' + T + 'float v = 0.f;' + '\n' + '\n' + T + 'switch(index)' + '\n' + T + '{' + '\n') if params != []: for param in params: label = getText(param, 'label') fcpp.write(T * 2 + 'case k' + label + ' : v = f' + label + '; break;' + '\n') fcpp.write(T * 2 + 'default: break;' + '\n' + T + '}' + '\n' + T + 'return v;' + '\n' + '}' + '\n' + '\n') #getParameterName fcpp.write( '//-------------------------------------------------------------' + '\n' + 'void ' + pluginLabel + '::getParameterName(long index, char *label)' + '\n' + '{' + '\n' + T + 'switch(index)' + '\n' + T + '{' + '\n') if params != []: for param in params: label = getText(param, 'label') caption = getText(param, 'caption') fcpp.write(T * 2 + 'case k' + label + ' : strcpy(label, "' + caption + '"); break;' + '\n') fcpp.write(T * 2 + 'default: break;' + '\n' + T + '}' + '\n' + '}' + '\n' + '\n') #getParameterDisplay fcpp.write( '//-------------------------------------------------------------' + '\n' + 'void ' + pluginLabel + '::getParameterDisplay(long index, char *text)' + '\n' + '{' + '\n' + T + 'switch(index)' + '\n' + T + '{' + '\n') if params != []: for param in params: label = getText(param, 'label') type = getText(param, 'type') if type == 'float': fcpp.write(T * 2 + 'case k' + label + ' : float2string(' + label + ', text); break;' + '\n') elif type == 'int': fcpp.write(T * 2 + 'case k' + label + ' : long2string(' + label + ', text); break;' + '\n') fcpp.write(T * 2 + 'default: break;' + '\n' + T + '}' + '\n' + '}' + '\n' + '\n') #getParameterLabel fcpp.write( '//-------------------------------------------------------------' + '\n' + 'void ' + pluginLabel + '::getParameterLabel(long index, char *label)' + '\n' + '{' + '\n' + T + 'switch(index)' + '\n' + T + '{' + '\n') if params != []: for param in params: label = getText(param, 'label') unit = getText(param, 'unit') fcpp.write(T * 2 + 'case k' + label + ' : strcpy(label, "' + unit + '"); break;' + '\n') fcpp.write(T * 2 + 'default: break;' + '\n' + T + '}' + '\n' + '}' + '\n' + '\n') #---------------------------------------------------------------------------- # processing # process fcpp.write( '#define XSPIF_WRITE_SAMPLE(dest,index,source) ((dest)[(index)] += (source))' + '\n' + '\n' + '//-------------------------------------------------------------' + '\n' + 'void ' + pluginLabel + '::process(float **inputs, float **outputs, long sampleframes)' + '\n' + '{' + '\n') i = 0 for pin in pins: dir = getText(pin, 'dir') if dir == 'In': label = getText(pin, 'label') channels = int(str(getText(pin, 'channels'))) for j in range(0, channels): i = i + 1 fcpp.write(T + 'float *' + label + str(j + 1) + ' = inputs[' + str(i - 1) + '];' + '\n') i = 0 for pin in pins: dir = getText(pin, 'dir') if dir == 'Out': label = getText(pin, 'label') channels = int(str(getText(pin, 'channels'))) for j in range(0, channels): i = i + 1 fcpp.write(T + 'float *' + label + str(j + 1) + ' = outputs[' + str(i - 1) + '];' + '\n') fcpp.write(T + 'vector_size = sampleframes;' + '\n' + '\n' + processCode + '\n' + '}' + '\n' + '\n' + '#undef XSPIF_WRITE_SAMPLE' + '\n' + '\n') # processReplacing fcpp.write( '#define XSPIF_WRITE_SAMPLE(dest,index,source) ((dest)[(index)] = (source))' + '\n' + '\n' + '//-------------------------------------------------------------' + '\n' + 'void ' + pluginLabel + '::processReplacing(float **inputs, float **outputs, long sampleframes)' + '\n' + '{' + '\n') i = 0 for pin in pins: dir = getText(pin, 'dir') if dir == 'In': label = getText(pin, 'label') channels = int(str(getText(pin, 'channels'))) for j in range(0, channels): i = i + 1 fcpp.write(T + 'float *' + label + str(j + 1) + ' = inputs[' + str(i - 1) + '];' + '\n') i = 0 for pin in pins: dir = getText(pin, 'dir') if dir == 'Out': label = getText(pin, 'label') channels = int(str(getText(pin, 'channels'))) for j in range(0, channels): i = i + 1 fcpp.write(T + 'float *' + label + str(j + 1) + ' = outputs[' + str(i - 1) + '];' + '\n') fcpp.write(T + 'vector_size = sampleframes;' + '\n' + '\n' + processCode + '\n' + '}' + '\n' + '\n' + '#undef XSPIF_WRITE_SAMPLE' + '\n' + '\n') #---------------------------------------------------------------------------- # main entry point fcpp.write(''' //------------------------------------------------------------- // main entry point //------------------------------------------------------------- bool oome = false; #if MAC #pragma export on #endif // prototype of the export function main #if BEOS #define main main_plugin extern "C" __declspec(dllexport) AEffect *main_plugin (audioMasterCallback audioMaster); #elif MACX #define main main_macho extern "C" AEffect *main_macho (audioMasterCallback audioMaster); #else AEffect *main (audioMasterCallback audioMaster); #endif AEffect *main (audioMasterCallback audioMaster) { // get vst version if (!audioMaster (0, audioMasterVersion, 0, 0, 0, 0)) return 0; // old version AudioEffect* effect = new ''' + pluginLabel + '''(audioMaster); if (!effect) return 0; if (oome) { delete effect; return 0; } return effect->getAeffect (); } #if MAC #pragma export off #endif #if WIN32 #include <windows.h> void* hInstance; BOOL WINAPI DllMain (HINSTANCE hInst, DWORD dwReason, LPVOID lpvReserved) { hInstance = hInst; return 1; } #endif ''') fcpp.close print T + 'done' # def file for windows: exports the plugins entry point print T + 'writing : ' + filename_prefix + '.vst.def' fdef = file(filename_prefix + '.vst.def', 'w') fdef.write('LIBRARY ' + pluginLabel + '\n' + "DESCRIPTION '" + pluginCaption + "'" + '\n' + 'EXPORTS main' + '\n') fdef.close print T + 'done' return
def generalCheck(domTree): """ method to check the validity of the XSPIF meta-plugin with respect to all constraints that are NOT handled by the DTD. """ # Get the elements pluginId = getText(domTree, 'plugId') pluginLabel = getText(domTree, 'label') pluginManufId = getText(domTree, "manufId") pluginCaption = getText(domTree, 'caption') pluginComments = getText(domTree, 'comment') pluginMaker = getText(domTree, 'maker') pluginCopyright = getText(domTree, 'copyright') pluginCode = getText(domTree, 'code') pins = domTree.getElementsByTagName('pin') params = domTree.getElementsByTagName('param') states = domTree.getElementsByTagName('state') controlouts = domTree.getElementsByTagName('controlout') callbacks = domTree.getElementsByTagName('callback') process = 0 processEvents = 0 instantiate = 0 deinstantiate = 0 activate = 0 deactivate = 0 for cb in domTree.getElementsByTagName('callback'): label = getText(cb, 'label') if ('process' == label): process = cb elif ('processEvents' == label): processEvents = cb elif ('instantiate' == label): instantiate = cb elif ('deinstantiate' == label): deinstantiate = cb elif ('activate' == label): activate = cb elif ('deactivate' == label): deactivate = cb else: print(T+'Warning: callback '+label+' is not known from ladspa.py') ############################################################ # Check the ID: for Id in [pluginId, pluginManufId]: if (len(Id) != 6)or(Id.find("'")!=0)or(Id.rfind("'")!=5): print(T+ 'Warning: '+Id+': you have to specify a 32 bit label.') print(T+ ' you can give it as 4 characters between simple quotes.') return -1 # Check every label is unique # Checking callback's label is unecessary because # already forced by the DTD labels = [] for tag in [pins, params, states, controlouts]: for el in tag: labels.append(getText(el,'label')) labels.append(pluginLabel) extraLabel = checkNotTwiceSameElement(labels) if (0 != extraLabel): print(T+"Error: label "+extraLabel+" was used more than once") return -1 # Check labels do not contain bad characters WrongCharList = [" ","&","'",'"',"&","#","@","-","*"] for myString in labels: if ("" == myString): print(T+'Error: One of the labels is null!!!') WrongChar = stringHasChar(myString, WrongCharList) if (WrongChar): print(T+"Error: Label '"+l+"' contains bad characher '"+WrongChar+"'") return -1 # check Min, max, and default and mapping values are OK. # Warning: min, max, and mapping are here supposed to be # REQUIRED by the DTD, thus we know that they exists. for tag in [params, controlouts]: if (0 != tag.length): for el in tag: if (0 != checkMinMaxDefault(el)): return -1 if (0 == pins.length): print(T+"Error: The plugin you build has no audio pin!") return -1 if (0 == callbacks.length): print(T+"Warning: The plugin you build has no callback!") if (0 == states.length): print(T+"Warning: The plugin you build has no state.") return
def write(domTree, filenamePrefix): """ This method is called by xspif2pd_linux.py It writes the PureData object's source file [filenamePrefix].pd_linux.c for the linux platform with the help of the DOM tree [x_plugin]. """ # Get the domTree direct childs pluginId = getText(domTree, 'plugId') pluginLabel = getText(domTree, 'label') pluginManufId = getText(domTree, "manufId") pluginCaption = getText(domTree, 'caption') pluginComments = getText(domTree, 'comment') pluginMaker = getText(domTree, 'maker') pluginCopyright = getText(domTree, 'copyright') pluginCode = getText(domTree, 'code') pins = domTree.getElementsByTagName('pin') params = domTree.getElementsByTagName('param') states = domTree.getElementsByTagName('state') controlouts = domTree.getElementsByTagName('controlout') callbacks = domTree.getElementsByTagName('callback') # Get the callbacks by their name instantiate = None deinstantiate = None process = None processEvents = None activate = None deactivate = None if callbacks == [] : print T+ "Warning: you haven't implemented any callback"+'\n' else : for callback in callbacks: text = str(getText(callback,'label')) if text == 'instantiate': instantiate = callback elif text == 'deinstantiate': deinstantiate = callback elif text == 'activate': activate = callback elif text == 'deactivate': deactivate = callback elif text == 'process': process = callback elif text == 'processEvents': print 'XSPIF Warning: callback "processEvents" not implemented for PD for now' #processEvents = callback else: print('Warning: callback ' + text +' is not known from pd.py' + '\n') # useful variables... separator = '\n' + '/****************************************************************/' + '\n' pluginLabelTilde = pluginLabel + '_tilde' t_pluginLabel = 't_' + pluginLabelTilde # make a list with pins in_ports = [] out_ports = [] for pin_node in pins: pin_label = getText(pin_node, 'label') pin_channels = int(getText(pin_node, 'channels')) pin_dir = getText(pin_node, 'dir') pin_caption = getText(pin_node, 'pin_caption') for i in range(1, pin_channels + 1): if ('In' == pin_dir): in_ports.append([pin_label + str(i), pin_caption]) elif ('Out' == pin_dir): out_ports.append([pin_label + str(i), pin_caption]) # ports clockwise sorted W.R.T. the graphical representation of the object. out_ports.reverse() out_ports_cw = out_ports[:] out_ports.reverse() ports_cw = in_ports + out_ports_cw #create output file(s) xspif_filename = (filenamePrefix + '.xspif') c_filename = (pluginLabel + '~.c') print T+'writing : '+c_filename fc = file(c_filename,'w') #---------------------------------------------------------------------------- # file header fc.write( '/****************************************************************' + '\n' + 'XSPIF: cross(X) Standard PlugIn Framework: ' + 'XSPIF to PD' + '\n' + T+ c_filename+'\n' + '\n' + pluginComments + '\n' + ' This file is generated automatically from the file: '+xspif_filename+'\n' + T+'plugin ID: '+pluginId+'\n' + T+'manufacturer ID: '+pluginManufId+'\n' + T+'maker: '+pluginMaker+'\n' + T+'copyright: '+pluginCopyright+'\n' + '****************************************************************/' + '\n' + '\n' + '\n' + '\n') #---------------------------------------------------------------------------- # includes fc.write('#include <stdlib.h>'+'\n' +'#include <string.h>'+'\n' +'#ifndef PD_VERSION'+'\n' +'#include "m_pd.h"'+'\n' +'#endif'+'\n') #---------------------------------------------------------------------------- # macros fc.write('\n' + '\n' + separator) fc.write(''' // Macro for getting the sample rate #undef XSPIF_GET_SAMPLE_RATE #define XSPIF_GET_SAMPLE_RATE()(sys_getsr()) // Macro for getting the vector_size #undef XSPIF_GET_VECTOR_SIZE #define XSPIF_GET_VECTOR_SIZE()(vector_size) // Macro for control outputs #undef XSPIF_CONTROLOUT #define XSPIF_CONTROLOUT(dest, index, value)(outlet_float(dest, value)) // Macros for checking parameter fit in its range #undef FIT_RANGE #define FIT_RANGE(value, min, max)(((value) < min) ? min : (((value) > max) ? max : (value))) ''') #---------------------------------------------------------------------------- # independant routines fc.write('\n' + '\n' + separator) fc.write('// add independant code here' + '\n') fc.write(pluginCode) #---------------------------------------------------------------------------- # Class declaration fc.write('\n' + '\n' + separator) fc.write('static t_class *' + pluginLabelTilde + '_class;' + '\n') fc.write('\n') # ...a t_object as first entry!! ... fc.write('typedef struct _' + pluginLabelTilde + ' {' + '\n' + T + 't_object x_obj;' + '\n') # ...the sample rate ... fc.write(T + 't_float sample_rate;' + '\n' + T + 't_int active;' + '\n') fc.write('\n') # ...a copy of the parameter for local use... # Note: don't care about type: all numbers are float in a PD graph fc.write(T + '// Internal copy of the parameters:\n') for p_node in params: p_label = getText(p_node, 'label') p_type = getText(p_node, 'type') fc.write(T + 't_float m_f' + p_label + ';' + '\n') fc.write('\n') # ... the states ...(except the code_states declared as global) fc.write(T + '// Internal states:\n') if ([] != states): for s_node in states: s_label = getText(s_node, 'label') s_type = getText(s_node, 'type') fc.write(T + s_type + ' ' + s_label + ';' + '\n') fc.write('\n') # ...the pointers to the controlouts... fc.write(T + '// Pointers to the outlets:\n') for c_node in controlouts: c_label = getText(c_node, 'label') c_type = getText(c_node, 'type') fc.write(T + c_type+' '+ c_label+'Value;'+ '\n') fc.write(T + 't_outlet *' + c_label + ';' + '\n') fc.write(T + 't_clock *p_'+c_label+'Clock;'+ '\n') fc.write('\n') # ... and a dummy variable for signal object. fc.write(T + '// Dummy variable needed for ~ objects:\n') fc.write(T + 't_sample dummy_f;' + '\n') fc.write('\n') fc.write('} t_' + pluginLabelTilde + ';' + '\n') #---------------------------------------------------------------------------- # Methods prototypes #---------------------------------------------------------------------------- fc.write('\n' + '\n' + separator) fc.write('// Prototypes' + '\n') if (deinstantiate): fc.write('void ' + pluginLabelTilde + '_free('+ t_pluginLabel +' *x);' + '\n') # if (activate): fc.write('void ' + pluginLabelTilde + '_activate('+ t_pluginLabel +' *x);' + '\n') # if (activate): fc.write('void ' + pluginLabelTilde + '_deactivate('+ t_pluginLabel +' *x);' + '\n') fc.write('static void ' + pluginLabelTilde + '_print('+ t_pluginLabel +' *x);' + '\n') #---------------------------------------------------------------------------- # Print methods to show infos about the plugin #---------------------------------------------------------------------------- # TODO: write the correct index for inlet number# fc.write('\n' + '\n' + separator) fc.write( ''' // Output information about the plugin static void ''' + pluginLabelTilde + '''_print('''+t_pluginLabel+''' *x){ // General info about the plugin post("\nThis file has been automatically generated with XSPIF: a (X)cross Standard PlugIn Framework from the XML file : ''' + xspif_filename +''' Plugin name : ''' + pluginCaption +''' Plugin label : ''' + pluginLabel + ''' Maker : ''' + pluginMaker + ''' Copyright : ''' + pluginCopyright + ''' Description :''' + pluginComments+ '''\n"); '''+ '\n'*2) fc.write('post("Control input(s):");' + '\n') fc.write('post("(Index for controls with a dedicated inlet are hinted with a #)");'+'\n') i = 0 # TODO: try to change increment: this is no true python for p_node in params: p_label = getText(p_node, 'label') p_min = getText(p_node, 'min') p_max = getText(p_node, 'max') p_type = getText(p_node, 'type') p_mapping = getText(p_node, 'mapping') p_unit = getText(p_node, 'unit') p_caption = getText(p_node, 'caption') p_comment = getText(p_node, 'comment') p_noinlet = getText(p_node, 'noinlet') if ('true' != p_noinlet): p_ind ='#'+ str(i) else: p_ind = str(i) fc.write('post(" ' + p_ind + ' ' + p_caption + ' = %f ('+ p_unit+')", x->m_f' +p_label+ ');' + '\n') fc.write('post(" '+ p_label + ', type: ' + p_type + ', in range [' + p_min + ' ; ' + p_max+ ']");' + '\n') fc.write('post(" with mapping (suggested): ' + p_mapping+'");' + '\n') fc.write('post(" Note:' +p_comment+'");' + '\n') i = i+1 fc.write('post("Control output(s):");' + '\n') i = 0 # TODO: try to change increment: this is no true python for c_node in controlouts: c_label = getText(c_node, 'label') c_min = getText(c_node, 'min') c_max = getText(c_node, 'max') c_type = getText(c_node, 'type') c_mapping = getText(c_node, 'mapping') c_unit = getText(c_node, 'unit') c_caption = getText(c_node, 'caption') c_comment = getText(c_node, 'comments') c_ind = str(i) fc.write('post(" #' + c_ind + ' ' + c_caption + ' (' + c_unit + ')");' + '\n') fc.write('post(" '+ c_label + ', type: ' + c_type + ', in range [' + c_min + ' ; ' + c_max+ ']");' + '\n') fc.write('post(" with mapping (suggested): ' + c_mapping+'");'+'\n') fc.write('post(" Note:' +c_comment+'");' + '\n') i = i+1 fc.write('post("Audio input(s):");' + '\n') for port in in_ports: ind = str(ports_cw.index(port) + 2) fc.write('post(" #' + ind + ' ' + port[0] + '");' + '\n') fc.write('post("Audio output(s):");' + '\n') for port in out_ports: ind = str(ports_cw.index(port) + 2) fc.write('post(" #' + ind + ' ' + port[0] + '");' + '\n') fc.write('if (x->active)' + '\n' +T+'post("Plugin ACTIVATED!");' + '\n' +'else'+'\n' +T+'post("Plugin DEACTIVATED!");' + '\n') fc.write('}' + '\n') #---------------------------------------------------------------------------- # Define methods for parameters which modifies any state #---------------------------------------------------------------------------- fc.write('\n') for p_node in params: p_label = getText(p_node, 'label') p_type = getText(p_node, 'type') p_min = getText(p_node, 'min') p_max = getText(p_node, 'max') p_code = getText(p_node, 'code') fc.write('\n' + '\n' + separator) fc.write('// Method responding to a change in parameter ' + p_label + '\n') fc.write('static void ' + pluginLabelTilde + '_' + p_label + '('+ t_pluginLabel +' *x, t_floatarg f){' + '\n' *2) if ('' != p_code): #... a copy of the states ... writeGetStates(fc, states) #... get pointers to the outlets... writeGetControlouts(fc, controlouts) # ...a copy of the parameters for local use... writeGetParams(fc, params) else: # ...just a copy of the parameter for update... fc.write(T +p_type + ' ' + p_label +' = x->m_f'+p_label+';' + '\n') fc.write('\n') fc.write(T + ' // Check the parameter fits its range and actualize it' + '\n' + T + p_label + ' = FIT_RANGE(f, '+p_min+', '+p_max+');'+'\n'*2) # Here is the state update code if ('' != p_code): fc.write('\n' + T + '{' + '\n' ) fc.write(p_code) fc.write('\n' + T + '}' + '\n'*2 ) # Update the states and THIS parameter in the plugin structure fc.write(T + '// Update ' + p_label + ' and states in the plugin structure:' + '\n') fc.write(T + 'x->m_f'+p_label+ ' = '+p_label+';' + '\n') if ('' != p_code): writeUpdateStates(fc, states) fc.write('}' + '\n') #---------------------------------------------------------------------------- # Function for controlouts called by clock if ([]!=controlouts): fc.write('\n' + '\n' + separator) fc.write('// Function for the control outlets called by clocks' + '\n') for c_node in controlouts: c_label = getText(c_node, 'label') fc.write('static void '+pluginLabelTilde+'_'+c_label+'(' +t_pluginLabel+' *x){'+'\n') fc.write(T+'outlet_float(x->'+c_label+', x->'+c_label+'Value);'+'\n') fc.write('}' + '\n') #---------------------------------------------------------------------------- # Function for controlouts : setting the clock fc.write('\n' + '\n' + separator) fc.write('// Function for controlouts : setting the clock' + '\n') fc.write('static void '+pluginLabelTilde+'_controlouts(' +t_pluginLabel+' *x, t_outlet *dest, t_float index, t_float value){'+'\n') for c_node in controlouts: c_label = getText(c_node, 'label') fc.write(T + 'if (dest == x->'+c_label+'){'+'\n') fc.write(T*2+'clock_delay(x->p_'+c_label+'Clock, index*1000/XSPIF_GET_SAMPLE_RATE());'+'\n') fc.write(T*2+'x->'+c_label+'Value = value;'+'\n') fc.write(T+'}' + '\n') fc.write('}' + '\n') #---------------------------------------------------------------------------- # Define once the XSPIF_WRITE_SAMPLE macro for 'run-replacing' process #---------------------------------------------------------------------------- fc.write('\n' + '\n' + separator) fc.write('// Macro for process-replacing' + '\n') fc.write('#undef XSPIF_WRITE_SAMPLE'+ '\n' '#define XSPIF_WRITE_SAMPLE(dest, index, value) ((dest)[(index)] = (value))'+ '\n' ) #---------------------------------------------------------------------------- # Define XSPIF_CONTROLOUTS so that it sets the clock, and does not # outlet_float during the thread-safe 'perform' routine. #---------------------------------------------------------------------------- fc.write('\n' + '\n' + separator) fc.write('// Macro for control outputs in the perform method : use clock' + '\n') fc.write('#undef XSPIF_CONTROLOUT'+ '\n' '#define XSPIF_CONTROLOUT(dest, index, value)(' +pluginLabelTilde+'_controlouts(x, dest, index*1000/XSPIF_GET_SAMPLE_RATE(), value)) '+ '\n') #---------------------------------------------------------------------------- # Plugin's 'perform' (DSP processing) function #---------------------------------------------------------------------------- fc.write('\n' + '\n' + separator) fc.write('t_int *' + pluginLabelTilde + '_perform(t_int *w)' + '\n' + '{' + '\n' + T + t_pluginLabel + ' *x = (' + t_pluginLabel + ' *)(w[1]);' + '\n') for port in ports_cw: fc.write(T + 't_sample *' + port[0] + ' = (t_sample *)(w[' + str(ports_cw.index(port) + 2) + ']);' + '\n') fc.write(T + 'int vector_size = (int)(w[' + str(len(ports_cw) + 2) + ']);' + '\n') # process only if plugin is active fc.write(T+'if (x->active)'+'\n'+T*2+'{'+'\n') # ... get a copy of the parameters ... writeGetParams(fc, params) # ...get a copy of the states ... writeGetStates(fc, states) # ...get the pointers to the outlets writeGetControlouts(fc, controlouts) # here is the DSP algorithm writeCallbackCode(fc, process) # Update the states in the plugin structure writeUpdateStates(fc, states) fc.write(T*2+'}'+'\n') fc.write(T + 'return (w+' + str(len(ports_cw) + 3) + ');' + '\n') fc.write('}' + '\n') #---------------------------------------------------------------------------- # Plugin's 'DSP' function: declare dsp method #---------------------------------------------------------------------------- fc.write('\n' + '\n' + separator) fc.write('// Plugin DSP method'+'\n') fc.write('void ' + pluginLabelTilde + '_dsp(' + t_pluginLabel +' *x, t_signal **sp)' + '\n') fc.write('{' + '\n') fc.write(T + 'dsp_add(' + pluginLabelTilde + '_perform, ' + str(len(ports_cw) + 2) + ', x,' + '\n' + T * 2 ) for port in ports_cw: fc.write(' sp[' + str(ports_cw.index(port)) + ']->s_vec,') fc.write('sp[0]->s_n);' + '\n') fc.write('}' + '\n') #---------------------------------------------------------------------------- # Plugin's new (instantiate) function #---------------------------------------------------------------------------- fc.write('\n' + '\n' + separator) fc.write('// Plugin new method' + '\n') fc.write('void *' + pluginLabelTilde + '_new(' + 't_symbol *s, int argc, t_atom *argv)' + '\n' #get the params as varargs + '{' + '\n' ) # declare variables fc.write(T+'int i;'+'\n' +T+'t_float val;'+'\n') # declare states if ([] != states): for s_node in states: s_label = getText(s_node, 'label') s_type = getText(s_node, 'type') if (-1 != s_type.find('*')): fc.write(T + s_type + ' ' + s_label + ' = NULL;' + '\n') else: fc.write(T + s_type + ' ' + s_label + ';' + '\n') fc.write('\n') # create the object fc.write(T + t_pluginLabel + ' *x = (' + t_pluginLabel + ' *)pd_new(' + pluginLabelTilde + '_class);' + '\n') # set the sample rate: fc.write(T + 'x->sample_rate = XSPIF_GET_SAMPLE_RATE();' + '\n') # Declare the inlet for the audio ports fc.write('\n') fc.write(T + '// Declare the in/out-lets for the audio ports' + '\n') fc.write(T + '// Beware: 1 inlet already declared in the "CLASS_DEFAULT"' + '\n') for port in in_ports[0 : (len(in_ports)-1)]: fc.write(T + 'inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);' + '\n') for port in out_ports: fc.write(T + 'outlet_new(&x->x_obj, &s_signal);' + '\n') # Declare inlets for params with no tag "noinlet" fc.write('\n') for p_node in params: p_label = getText(p_node, 'label') p_noinlet = getText(p_node, 'noinlet') if ('true' != p_noinlet): # if ('' != getText(p_node, 'code')): # declare a new inlet (with dedicated method) # TODO: make it no only for &s_float taking param type in account fc.write(T + '// ' + p_label + ' calls a dedicated method' + '\n') fc.write(T + 'inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, ' + 'gensym("' + p_label + '")' + ');' + '\n') # else: # #declare un floatinlet_new (without dedicated method) # fc.write(T + '// ' + p_label + ' is written directly' + '\n') # fc.write(T + 'floatinlet_new (&x->x_obj, &x->m_f' + p_label + ');' + '\n') # Declare outlets for controlouts fc.write('\n') for c_node in controlouts: c_label = getText(c_node, 'label') # TODO: make it no only for &s_float taking param type in account fc.write(T + '// controlout ' + c_label + '\n') fc.write(T+'x->'+c_label+' = outlet_new(&x->x_obj, &s_float);'+'\n') fc.write(T+'x->p_'+c_label+'Clock = clock_new(x, (t_method)'+pluginLabelTilde+'_'+c_label+');'+'\n') # Put the instanciation code here: writeCallbackCode(fc, instantiate) # Update the states in the plugin structure writeUpdateStates(fc, states) # Getting the parameters given at instanciation # Only the exact number of parameters can be given here if ([]!=params): fc.write( '\n'+T+'// Get the parameters'+'\n' +T+'switch (argc)'+'\n' +T*2+'{' + '\n' +T*2+'case 0:'+'\n') for p_node in params: p_label = getText(p_node, 'label') p_code = getText(p_node, 'code') p_default = getText(p_node, 'default') fc.write(T*4+pluginLabelTilde+'_' +p_label+'(x, '+p_default+');'+'\n') fc.write( T*3+'break;'+'\n' +T*2+'case '+str(len(params))+':'+'\n'+T*3+'{'+'\n' +T*4+'// Check all parameters are float'+'\n' +T*4+'for (i=0; i <= argc-1;i++)'+'\n' +T*5+'if (argv[i].a_type != A_FLOAT)'+'\n' +T*6+'{'+'\n' +T*7+'post("'+pluginLabelTilde+' : wrong arguments");'+'\n' +T*7+'return NULL;'+'\n' +T*6+'}'+'\n') p_index = 0 for p_node in params: p_label = getText(p_node, 'label') p_min = getText(p_node, 'min') p_max = getText(p_node, 'max') p_code = getText(p_node, 'code') p_default = getText(p_node, 'default') fc.write(T*4+'val= atom_getfloatarg('+str(p_index)+', argc, argv);'+'\n') fc.write(T*4+pluginLabelTilde+'_' +p_label+'(x, val);'+'\n') p_index += 1 fc.write(T*3+'}'+'\n' +T*3+'break;'+'\n') fc.write(T*2+'default:'+'\n' +T*3+'{'+'\n' +T*4+'post( "'+pluginLabel+' : error in the number of arguments ( %d )", argc );' + '\n' +T*4+'return NULL;'+ '\n' +T*3+'}' + '\n' +T*2+'}' + '\n') # call method activate fc.write('\n') # if (activate): fc.write(T + pluginLabelTilde + '_activate(x);' + '\n') fc.write(T + 'return (void *)x;' + '\n') fc.write('}' + '\n') #--------------------------------------------------------------------------- # Plugin activate function #--------------------------------------------------------------------------- # if (activate): fc.write('\n' + '\n' + separator) fc.write('// Initialise and activate a plugin instance.'+ '\n') fc.write( 'void ' + pluginLabelTilde + '_activate('+ t_pluginLabel +' *x) {' + '\n' * 2) # Get the states and params writeGetStates(fc, states) writeGetParams(fc, params) #... get pointers to the outlets... writeGetControlouts(fc, controlouts) # Put the activate code here: writeCallbackCode(fc, activate) # Update the states in the plugin structure writeUpdateStates(fc, states) # Activate the plugin fc.write(T+'x->active = 1;'+'\n') fc.write('}' + '\n') #--------------------------------------------------------------------------- # Plugin deactivate function #--------------------------------------------------------------------------- # if (activate): fc.write('\n' + '\n' + separator) fc.write('// Deactivate a plugin instance (bypass).'+ '\n') fc.write( 'void ' + pluginLabelTilde + '_deactivate('+ t_pluginLabel +' *x) {' + '\n' * 2) # Get the states and params writeGetStates(fc, states) writeGetParams(fc, params) #... get pointers to the outlets... writeGetControlouts(fc, controlouts) # Put the activate code here: writeCallbackCode(fc, deactivate) # Update the states in the plugin structure writeUpdateStates(fc, states) # Deactivate the plugin fc.write(T+'x->active = 0;'+'\n') fc.write('}' + '\n') #---------------------------------------------------------------------------- # Plugin's free (deinstantiate) method # (only implemented if the plugin need it) #---------------------------------------------------------------------------- if (deinstantiate or [] != controlouts): fc.write('\n' + '\n' + separator) fc.write('// Plugin cleanup method' + '\n') fc.write('void ' + pluginLabelTilde + '_free('+t_pluginLabel+' *x){' + '\n') # Get the states, so that any structure can be deleted fom memory writeGetStates(fc, states) writeGetParams(fc, params) if (deinstantiate): # Put the deinstantiate code here writeCallbackCode(fc, deinstantiate) for c_node in controlouts: c_label = getText(c_node, 'label') fc.write(T+'clock_free(x->p_'+c_label+'Clock);'+'\n') fc.write('}' + '\n') #---------------------------------------------------------------------------- # Plugin's setup function #---------------------------------------------------------------------------- fc.write('\n' + '\n' + separator) fc.write('// Plugin setup method' + '\n') fc.write('void ' + pluginLabelTilde + '_setup(void) {' + '\n' + T + pluginLabelTilde + '_class = class_new(gensym("' + pluginLabel+ '~"),' + '\n' + T *2 + '(t_newmethod)' + pluginLabelTilde + '_new,' + '\n') #if plugin needs a destructor, it should be declared here if (deinstantiate or [] != controlouts): fc.write(T *2 + '(t_method)' + pluginLabelTilde + '_free,' + '\n') else: fc.write(T *2 + '0,' + '\n') fc.write(T *2 + 'sizeof(' + t_pluginLabel + '),' + '\n' + T *2 + 'CLASS_DEFAULT,' + '\n' + T *2 + 'A_GIMME, 0);' + '\n') #a list of argument can be given representing all parameters fc.write('\n') fc.write(T + 'class_addmethod(' + pluginLabelTilde + '_class,' + '\n' + T*2 + '(t_method)' + pluginLabelTilde + '_dsp, gensym("dsp"), 0);' + '\n') fc.write(T + 'class_addmethod(' + pluginLabelTilde + '_class,' + '\n' + T*2 + '(t_method)' + pluginLabelTilde + '_print, gensym("print"), 0);' + '\n') fc.write(T + 'class_addmethod(' + pluginLabelTilde + '_class,' + '\n' + T*2 + '(t_method)' + pluginLabelTilde + '_activate, gensym("on"), 0);' + '\n') fc.write(T + 'class_addmethod(' + pluginLabelTilde + '_class,' + '\n' + T*2 + '(t_method)' + pluginLabelTilde + '_deactivate, gensym("off"), 0);' + '\n') fc.write('\n') for p_node in params: p_label = getText(p_node, 'label') # if ('' != getText(p_node, 'code')): # declare a new inlet (with dedicated method) # TODO: make it no only for &s_float taking param type in account fc.write(T + '// Declare a method for the parameter ' + p_label + '\n') fc.write(T + 'class_addmethod(' + pluginLabelTilde + '_class,' + '\n' + T*2 + '(t_method)' + pluginLabelTilde + '_' + p_label + ', gensym("' + p_label + '"), A_FLOAT, 0);' + '\n') fc.write(T + '' + '\n') fc.write(T + 'CLASS_MAINSIGNALIN(' + pluginLabelTilde + '_class, ' + t_pluginLabel + ', dummy_f);' + '\n') fc.write('}' + '\n') # float ParameterCheck (char* name, float value, float min, float max ) # { # if (value > max) # { # printf("Parameter: %s out of range [%.1f...%.1f]. Value limited.\n", name, # min, max ); # return(max); # } # if(value < min) # { # printf("Parameter: %s out of range [%.1f...%.1f]. Value limited.\n", name, # min, max ); # return(min); # } # return(value); # } fc.close print T+'done' #--------------------------------------------------------------------------- # MAKFILE #--------------------------------------------------------------------------- print T+'writing : Makefile' fm = file('Makefile','w') fm.write(''' NAME = '''+pluginLabel+''' current: echo make pd_linux, pd_nt, pd_irix5, or pd_irix6 clean: ; rm -f *.pd_linux *.o # ----------------------- NT ----------------------- pd_nt: $(NAME)~.dll .SUFFIXES: .dll # Change this to match you pd path: PD_NT_PATH = "C:/pd" PDNTCFLAGS = /W3 /WX /DNT /DPD /nologo VC="C:\Program Files\Microsoft Visual Studio\Vc98" PDNTINCLUDE = /I. /I\tcl\include /I$(PD_NT_PATH)\src /I$(VC)\include PDNTLDIR = $(VC)\lib PDNTLIB = $(PDNTLDIR)\libc.lib $(PDNTLDIR)\oldnames.lib $(PDNTLDIR)\kernel32.lib $(PD_NT_PATH)/bin/pd.lib .c.dll: cl $(PDNTCFLAGS) $(PDNTINCLUDE) /c $(NAME)~.c link /dll /export:$(NAME)_tilde_setup $(NAME)~.obj $(PDNTLIB) # ----------------------- IRIX 5.x ----------------------- pd_irix5: $(NAME)~.pd_irix5 .SUFFIXES: .pd_irix5 SGICFLAGS5 = -o32 -DPD -DUNIX -DIRIX -O2 SGIINCLUDE = -I../../src/ .c.pd_irix5: cc $(SGICFLAGS5) $(SGIINCLUDE) -o $*.o -c $*.c ld -elf -shared -rdata_shared -o $*.pd_irix5 $*.o rm $*.o # ----------------------- IRIX 6.x ----------------------- pd_irix6: $(NAME)~.pd_irix6 .SUFFIXES: .pd_irix6 SGICFLAGS6 = -n32 -DPD -DUNIX -DIRIX -DN32 -woff 1080,1064,1185 \ -OPT:roundoff=3 -OPT:IEEE_arithmetic=3 -OPT:cray_ivdep=true \ -Ofast=ip32 .c.pd_irix6: cc $(SGICFLAGS6) $(SGIINCLUDE) -o $*.o -c $*.c ld -IPA -n32 -shared -rdata_shared -o $*.pd_irix6 $*.o rm $*.o # ----------------------- LINUX i386 ----------------------- pd_linux: $(NAME)~.pd_linux .SUFFIXES: .pd_linux LINUXCFLAGS = -DPD -O6 -funroll-loops -fomit-frame-pointer \ -Wall -W -Wshadow -Wstrict-prototypes \ -Wno-unused -Wno-parentheses -Wno-switch LINUXINCLUDE = -I/usr/local/lib/pd/include .c.pd_linux: cc $(LINUXCFLAGS) $(LINUXINCLUDE) -o $*.o -c $*.c ld -export_dynamic -shared -o $*.pd_linux $*.o -lc -lm strip --strip-unneeded $*.pd_linux rm $*.o # ----------------------- Mac OSX ----------------------- pd_darwin: $(NAME)~.pd_darwin .SUFFIXES: .pd_darwin DARWINCFLAGS = -DPD -O2 -Wall -W -Wshadow -Wstrict-prototypes \ -Wno-unused -Wno-parentheses -Wno-switch .c.pd_darwin: cc $(DARWINCFLAGS) $(LINUXINCLUDE) -o $*.o -c $*.c cc -bundle -undefined suppress -flat_namespace -o $*.pd_darwin $*.o rm -f $*.o ../$*.pd_darwin ln -s $*/$*.pd_darwin .. ''') fm.close print T+'done' return
def generalCheck(domTree): """ method to check the validity of the XSPIF meta-plugin with respect to all constraints that are NOT handled by the DTD. """ # Get the elements pluginId = getText(domTree, 'plugId') pluginLabel = getText(domTree, 'label') pluginManufId = getText(domTree, "manufId") pluginCaption = getText(domTree, 'caption') pluginComments = getText(domTree, 'comment') pluginMaker = getText(domTree, 'maker') pluginCopyright = getText(domTree, 'copyright') pluginCode = getText(domTree, 'code') pins = domTree.getElementsByTagName('pin') params = domTree.getElementsByTagName('param') states = domTree.getElementsByTagName('state') controlouts = domTree.getElementsByTagName('controlout') callbacks = domTree.getElementsByTagName('callback') process = 0 processEvents = 0 instantiate = 0 deinstantiate = 0 activate = 0 deactivate = 0 for cb in domTree.getElementsByTagName('callback'): label = getText(cb, 'label') if ('process' == label): process = cb elif ('processEvents' == label): processEvents = cb elif ('instantiate' == label): instantiate = cb elif ('deinstantiate' == label): deinstantiate = cb elif ('activate' == label): activate = cb elif ('deactivate' == label): deactivate = cb else: print(T + 'Warning: callback ' + label + ' is not known from ladspa.py') ############################################################ # Check the ID: for Id in [pluginId, pluginManufId]: if (len(Id) != 6) or (Id.find("'") != 0) or (Id.rfind("'") != 5): print(T + 'Warning: ' + Id + ': you have to specify a 32 bit label.') print( T + ' you can give it as 4 characters between simple quotes.' ) return -1 # Check every label is unique # Checking callback's label is unecessary because # already forced by the DTD labels = [] for tag in [pins, params, states, controlouts]: for el in tag: labels.append(getText(el, 'label')) labels.append(pluginLabel) extraLabel = checkNotTwiceSameElement(labels) if (0 != extraLabel): print(T + "Error: label " + extraLabel + " was used more than once") return -1 # Check labels do not contain bad characters WrongCharList = [" ", "&", "'", '"', "&", "#", "@", "-", "*"] for myString in labels: if ("" == myString): print(T + 'Error: One of the labels is null!!!') WrongChar = stringHasChar(myString, WrongCharList) if (WrongChar): print(T + "Error: Label '" + l + "' contains bad characher '" + WrongChar + "'") return -1 # check Min, max, and default and mapping values are OK. # Warning: min, max, and mapping are here supposed to be # REQUIRED by the DTD, thus we know that they exists. for tag in [params, controlouts]: if (0 != tag.length): for el in tag: if (0 != checkMinMaxDefault(el)): return -1 if (0 == pins.length): print(T + "Error: The plugin you build has no audio pin!") return -1 if (0 == callbacks.length): print(T + "Warning: The plugin you build has no callback!") if (0 == states.length): print(T + "Warning: The plugin you build has no state.") return
def write(plugin, filename_prefix): fcpp = file(filename_prefix + '.au.cpp', 'w') xml_filename = filename_prefix + ".xspif" print T + 'writing : ' + filename_prefix + '.au.cpp' ######## independent code here ############ # Variables plugId = getText(plugin, 'plugId') manufId = getText(plugin, 'manufId') maker = getText(plugin, 'maker') pluginLabel = getText(plugin, 'label') pluginLabel.replace(' ', '') #remove spaces in pluginLabel.title() # forces the first letter to upper case copyright = getText(plugin, 'copyright') comment = getText(plugin, 'comment') + '\n' pluginCode = getText(plugin, 'code') + '\n' pluginCaption = getText(plugin, 'caption') params = plugin.getElementsByTagName('param') states = plugin.getElementsByTagName("state") pins = plugin.getElementsByTagName("pin") callbacks = plugin.getElementsByTagName('callback') instantiate = None deinstantiate = None process = None processEvents = None activate = None deactivate = None if callbacks == []: print T + "Warning: you haven't implemented any callback" + '\n' else: for callback in callbacks: text = str(getText(callback, 'label')) if text == 'instantiate': instantiate = callback if text == 'deinstantiate': deinstantiate = callback if text == 'activate': activate = callback if text == 'deactivate': deactivate = callback if text == 'process': process = callback if text == 'processEvents': print 'XSPIF Warning: callback "processEvents" not implemented in VST for now' #processEvents = callback instantiateCode = getText(instantiate, 'code') deinstantiateCode = getText(deinstantiate, 'code') activateCode = getText(activate, 'code') deactivateCode = getText(deactivate, 'code') processCode = getText(process, 'code') #---------------------------------------------------------------------------- # CPP file #---------------------------------------------------------------------------- #---------------------------------------------------------------------------- # file header fcpp.write( '/****************************************************************\n' + 'XSPIF: cross(X) Standard PlugIn Framework: ' + 'XSPIF to AudioUnits\n' + T + filename_prefix + '.au.cpp' + '\n\n' + comment + ' This file is generated automatically from the file: ' + xml_filename + '\n' + ' DO NOT EDIT BY HAND' + '\n' + T + 'plugin ID: ' + plugId + '\n' + T + 'manufacturer ID: ' + manufId + '\n' + T + 'maker: ' + maker + '\n' + T + 'copyright: ' + copyright + '\n' + ' ***************************************************************/\n' + '\n') #includes and independent code fcpp.write( '\n' + '#include <AUEffectBase.h>' + '\n' + '\n' + '// XSPIF macros' + '\n' + '#define XSPIF_GET_SAMPLE_RATE() (GetSampleRate())' + '\n' + '#define XSPIF_GET_VECTOR_SIZE() (vector_size)' + '\n' + '#define XSPIF_CONTROLOUT() (// NO control out until macosx 10.3)' + '\n' + '\n' + '// <from ' + xml_filename + '>' + '\n' + pluginCode + '\n' + '// </from ' + xml_filename + '>' + '\n') #---------------------------------------------------------------------------- # Class declaration (TODO:change the base according to the purpose) fcpp.write( 'class ' + pluginLabel + ': public AUEffectBase' + '\n' + '{' + '\n' + 'public:' + '\n' + T + pluginLabel + '(AudioUnit component);' + '\n' + T + '~' + pluginLabel + '();' + '\n' + T + '' + '\n' + T + 'virtual OSStatus ProcessBufferLists( AudioUnitRenderActionFlags & ioActionFlags,' + '\n' + T + ' const AudioBufferList & inBuffer,' + '\n' + T + ' AudioBufferList & outBuffer,' + '\n' + T + ' UInt32 inFramesToProcess);' + '\n' + T + '' + '\n' + T + 'virtual UInt32 SupportedNumChannels(const AUChannelInfo** outInfo);' + '\n' + T + 'virtual ComponentResult Initialize();' + '\n' + T + 'virtual ComponentResult GetParameterInfo( AudioUnitScope inScope,' + '\n' + T + ' AudioUnitParameterID inParameterId,' + '\n' + T + ' AudioUnitParameterInfo &outParameterInfo);' + '\n' + T + '' + '\n' + 'private:' + '\n') # declare params if params != []: fcpp.write(T + '//params' + '\n' + T + 'enum Parameters' + '\n' + T + '{' + '\n') for param in params: label = getText(param, 'label') fcpp.write(T * 2 + 'k' + label + ',' + '\n') fcpp.write(T + '' + '\n' + T * 2 + 'kNumParams' + '\n' + T + '};' + '\n' + '\n') for param in params: label = getText(param, 'label') default = getText(param, 'default') min = getText(param, 'min') max = getText(param, 'max') fcpp.write(T + 'static const float ' + label + 'Default = ' + default + ';' + '\n' + T + 'static const float ' + label + 'Min = ' + min + ';' + '\n' + T + 'static const float ' + label + 'Max = ' + max + ';' + '\n' + T + 'float ' + label + ';' + '\n' + '\n') # declare supported number of channels numInputs = numOutputs = 0 for pin in pins: dir = getText(pin, 'dir') channels = int(str(getText(pin, 'channels'))) if dir == 'In': numInputs += channels elif dir == 'Out': numOutputs += channels if numInputs < numOutputs: numchannels = numInputs else: numchannels = numOutputs numchannels = str(numchannels) numInputs = str(numInputs) numOutputs = str(numOutputs) fcpp.write( T + 'enum {' + '\n' + T * 2 + 'kNumSupportedNumChannels = ' + numchannels + '\n' + T + '};' + '\n' + '\n' + T + 'static AUChannelInfo m_aobSupportedNumChannels[kNumSupportedNumChannels];' + '\n' + '\n') # process declaration fcpp.write(T + 'OSStatus Process(const AudioBufferList& obInBuffers,' + '\n' + T + ' AudioBufferList& obOutBuffers,' + '\n' + T + ' UInt32 inFramesToProcess,' + '\n' + T + ' AudioUnitRenderActionFlags& ioactionFlags);' + '\n' + '\n') # states if states != []: fcpp.write(T + '// states' + '\n') for state in states: label = getText(state, 'label') type = getText(state, 'type') fcpp.write(T + type + ' ' + label + ';' + '\n') fcpp.write('\n' + T + 'long vector_size;' + '\n' + '};' + '\n' + '\n') #---------------------------------------------------------------------------- # CPP file #-------------------------------------------------------------------------- fcpp.write(""" //---------------------------------------------------------------------------- // IMPLEMENTATION //-------------------------------------------------------------------------- """) #-------------------------------------------------------------------------- fcpp.write('//------------------------------------------------------' + '\n' + 'COMPONENT_ENTRY(' + pluginLabel + ')' + '\n' + '\n') #-------------------------------------------------------------------------- # Topology numInputs = numOutputs = 0 for pin in pins: dir = getText(pin, 'dir') channels = int(str(getText(pin, 'channels'))) if dir == 'In': numInputs += channels elif dir == 'Out': numOutputs += channels numInputs = str(numInputs) numOutputs = str(numOutputs) fcpp.write('#define NUM_INPUTS ' + numInputs + '\n' + '#define NUM_OUTPUTS ' + numOutputs + '\n') fcpp.write('//------------------------------------------------------' + '\n' + 'AUChannelInfo ' + pluginLabel + '::m_aobSupportedNumChannels[' + pluginLabel + '::kNumSupportedNumChannels] = {{' + numInputs + ',' + numOutputs + '}};' + '\n' + '\n') #-------------------------------------------------------------------------- # Plugin Class # Plugin Constructor fcpp.write('//------------------------------------------------------' + '\n' + pluginLabel + '::' + pluginLabel + '(AudioUnit component): AUEffectBase(component)' + '\n' + '{' + '\n' + T + 'CreateElements();' + '\n' + '\n') fcpp.write('\n' + T + '// instantiate callback' + '\n' + T + instantiateCode + '\n' + '\n') if params != []: for param in params: label = getText(param, 'label') fcpp.write(T + 'SetParameter(k' + label + ',' + label + 'Default);' + '\n') fcpp.write('}' + '\n' + '\n') # Plugin Deconstructor fcpp.write('//------------------------------------------------------' + '\n' + pluginLabel + '::~' + pluginLabel + '()' + '\n' + '{' + '\n') fcpp.write('\n' + T + '// deinstantiate callback' + '\n' + T + deinstantiateCode + '\n' + '\n') fcpp.write('}' + '\n' + '\n') # Plugin infos fcpp.write('//------------------------------------------------------' + '\n' + 'UInt32 ' + pluginLabel + '::SupportedNumChannels(const AUChannelInfo **outInfo)' + '\n' + '{' + '\n' + T + 'if(outInfo != NULL)' + '\n' + T * 2 + '*outInfo = &m_aobSupportedNumChannels[0];' + '\n' + T + 'return kNumSupportedNumChannels;' + '\n' + '}' + '\n' + '\n') fcpp.write( '//------------------------------------------------------' + '\n' + 'ComponentResult ' + pluginLabel + '::GetParameterInfo(AudioUnitScope inScope,' + '\n' + ' AudioUnitParameterID inParameterID,' + '\n' + ' AudioUnitParameterInfo &outParameterInfo)' + '\n' + '{' + '\n' + T + 'if(inScope != kAudioUnitScope_Global)' + '\n' + T * 2 + 'return kAudioUnitErr_InvalidParameter;' + '\n' + '\n' + T + 'ComponentResult result = noErr;' + '\n' + '\n' + T + 'outParameterInfo.flags = kAudioUnitParameterFlag_IsWritable | kAudioUnitParameterFlag_IsReadable | kAudioUnitParameterFlag_Global;' + '\n' + '\n' + T + 'char *pcName = outParameterInfo.name;' + '\n' + '\n' + T + 'switch(inParameterID)' + '\n' + T + '{' + '\n') if params != []: for param in params: label = getText(param, 'label') caption = getText(param, 'caption') fcpp.write( T * 2 + 'case k' + label + ':' + '\n' + T * 3 + 'strcpy(pcName,"' + caption + '");' + '\n' + T * 3 + 'outParameterInfo.unit = kAudioUnitParameterUnit_Generic;' + '\n' + T * 3 + 'outParameterInfo.minValue = ' + label + 'Min;' + '\n' + T * 3 + 'outParameterInfo.maxValue = ' + label + 'Max;' + '\n' + T * 3 + 'outParameterInfo.defaultValue = ' + label + 'Default;' + '\n' + T * 3 + 'break;' + '\n' + '\n') fcpp.write(T * 2 + 'default:' + '\n' + T * 3 + 'result = kAudioUnitErr_InvalidParameter;' + '\n' + T * 3 + 'break;' + '\n' + T + '}' + '\n' + '\n' + T + 'return result;' + '\n' + T + '}' + '\n') # Plugin suspend and resume fcpp.write('//------------------------------------------------------' + '\n' + 'ComponentResult ' + pluginLabel + '::Initialize()' + '\n' + '{' + '\n' + T + 'return noErr;' + '\n' + '}' + '\n') #-------------------------------------------------------------------------- # processing fcpp.write( '//------------------------------------------------------' + '\n' + 'OSStatus ' + pluginLabel + '::ProcessBufferLists(AudioUnitRenderActionFlags &ioActionFlags,' + '\n' + ' const AudioBufferList &inBuffer,' + '\n' + ' AudioBufferList &outBuffer,' + '\n' + ' UInt32 inFramesToProcess)' + '\n' + '{' + '\n' + T + 'ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;' + '\n' + '\n' + T + 'UInt32 uiInBuffers = inBuffer.mNumberBuffers;' + '\n' + T + 'UInt32 uiOutBuffers = outBuffer.mNumberBuffers;' + '\n' + '\n' + T + 'if(uiInBuffers != NUM_INPUTS || uiOutBuffers != NUM_OUTPUTS)' + '\n' + T * 2 + 'return kAudioUnitErr_FormatNotSupported;' + '\n' + '\n' + T + 'return Process(inBuffer, outBuffer, inFramesToProcess, ioActionFlags);' + '\n' + '}' + '\n' + '\n') fcpp.write( '#define XSPIF_WRITE_SAMPLE(dest,index,source) ((dest)[(index)] = (source))' + '\n' + '\n') fcpp.write( '//------------------------------------------------------' + '\n' + 'OSStatus ' + pluginLabel + '::Process(const AudioBufferList &obInBuffers,' + '\n' + ' AudioBufferList &obOutBuffers,' + '\n' + ' UInt32 inFramesToProcess,' + '\n' + ' AudioUnitRenderActionFlags &ioActionFlags)' + '\n' + '{' + '\n' + T + 'ioActionFlags &= ~kAudioUnitRenderAction_OutputIsSilence;' + '\n' + '\n') for i in range(0, int(numInputs)): fcpp.write(T + 'const AudioBuffer &obInBuffer' + str(i) + ' = obInBuffers.mBuffers[' + str(i) + '];' + '\n') for i in range(0, int(numOutputs)): fcpp.write(T + 'AudioBuffer &obOutBuffer' + str(i) + ' = obOutBuffers.mBuffers[' + str(i) + '];' + '\n') i = 0 for pin in pins: dir = getText(pin, 'dir') if dir == 'In': i = i + 1 label = getText(pin, 'label') channels = int(str(getText(pin, 'channels'))) for j in range(0, channels): fcpp.write(T + 'const Float32 *' + label + str(i + j) + ' = (const Float32 *)obInBuffer' + str(i + j - 1) + '.mData;' + '\n') i = 0 for pin in pins: dir = getText(pin, 'dir') if dir == 'Out': i = i + 1 label = getText(pin, 'label') channels = int(str(getText(pin, 'channels'))) for j in range(0, channels): fcpp.write(T + 'Float32 *' + label + str(i + j) + ' = (Float32 *)obOutBuffer' + str(i + j - 1) + '.mData;' + '\n') fcpp.write(T + 'vector_size = inFramesToProcess;' + '\n' + '\n') if params != []: fcpp.write(T + '//retrieving params' + '\n') for param in params: label = getText(param, 'label') fcpp.write(T + label + ' = GetParameter(k' + label + ');' + '\n') fcpp.write(T + '//updating states' + '\n') for param in params: paramCode = getText(param, 'code') fcpp.write(T + paramCode + '\n') fcpp.write(T + '//****************************************' + '\n' + processCode + '\n' + '//****************************************' + '\n' + '\n' + T + 'return noErr;' + '\n' + '}' + '\n') fcpp.close print T + 'done' #-------------------------------------------------------------------------- # r fr = file(filename_prefix + '.au.r', 'w') print T + 'writing : ' + filename_prefix + '.au.r' fr.write( '#include <AudioUnit/AudioUnit.r>' + '\n' + '\n' + '// Note that resource IDs must be spaced 2 apart for the ' + "'STR'" + ' name and description' + '\n' + '#define kAudioUnitResID_' + pluginLabel + ' 10000' + '\n' + '\n' + '// So you need to define these appropriately for your audio unit.' + '\n' + "// For the name the convention is to provide your company name and end it with a ':'," + '\n' + '// then provide the name of the AudioUnit.' + '\n' + '// The Description can be whatever you want.' + '\n' + '// For an effect unit the Type and SubType should be left the way they are defined here...' + '\n' + '//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' + '\n' + '// SampleEffectUnit' + '\n' + '#define RES_ID kAudioUnitResID_' + pluginLabel + '\n' + '#define COMP_TYPE ' + "'aufx'" + '\n' + '#define COMP_SUBTYPE ' + plugId + '\n' + '#define COMP_MANUF ' + manufId + '\n' + '#define VERSION 0x00010000' + '\n' + '#define NAME "' + maker + ': ' + pluginCaption + '"' + '\n' + '#define DESCRIPTION "' + pluginCaption + '"' + '\n' + '#define ENTRY_POINT "' + pluginLabel + 'Entry"' + '\n' + '\n' + '#include "AUResources.r"' + '\n') fr.close print T + 'done' fexp = file(filename_prefix + '.exp', 'w') print T + 'writing : ' + filename_prefix + '.exp' fexp.write('_' + pluginLabel + 'Entry') fexp.close print T + 'done' return