WDL-OL/IPlug project for gen~ code export

oli larkin's icon

oli larkin

2月 28 2014 | 9:20 午後

I've made an IPlug project that can be used to quickly compile a gen~ patch to VST2/VST3/AU/RTAS/AAX/APP/IOS* and to add a gui etc.

It's an early version, but it might be handy for someone. Please read the IPlugExamples.rtf in detail before asking about how to compile it.

*note: ProTools formats require Avid developer account and IOS project is out-dated (only works with IOS4 sdk on Xcode3)

www.olilarkin.co.uk

eltopo's icon

eltopo

3月 01 2014 | 10:25 午後

Thank you very much OLI!
PS: It would be very useful a sample IPlugGen project, implementing a basic gui and some control.

oli larkin's icon

oli larkin

3月 02 2014 | 9:07 午前

have a look at the IPlug example projects, which feature lots of different kinds of control

dtr's icon

dtr

3月 02 2014 | 9:35 午前

Greatness!

alfonso santimone's icon

alfonso santimone

3月 02 2014 | 12:18 午後

Wow! great stuff Oli! many thanks!

thetechnobear's icon

thetechnobear

3月 02 2014 | 1:08 午後

this looks like a great idea!

I have a naive question, I'm looking at developing a VST (that talks to a hardware device, and outputs midi). I was previously assuming Id build it in Juce...
but having looked at IPlug, it seems to offer much the same facilities.

Ive looked around, and cannot find any real comparison about why to choose IPlug/Wdl over Juce, or vice versa ... is there some comparison somewhere, or a guide about which to choose.

the only reason Ive found so far, is due to commercial licensing of Juce - something I don't think is applicable for me... though of course avoiding is a good thing generally :)

(just to be clear, I'm not after X is better than Y, I'm sure both are great... but wonder how to make an informed choice)

oli larkin's icon

oli larkin

3月 02 2014 | 1:46 午後

A few thoughts (I use both)

JUCE:

has good documentation
has consistent and high quality code
has a big user base
very good vector graphics capabilities
has a rather nice project generator (the introjucer)
much more than just plugins
doesn't impose particular thread safety technique

WDL-OL/IPlug:

Easier for C++ beginners
Concise plugin implementation
Many helpful scripts
IDE projects setup for debugging in lots of common hosts
Free
Possible to fork it and "own" your version
Perhaps less of a moving target
Uses rather brutal mutex locks for thread safety

I think JUCE is well worth the money, but i like the simplicity of implementing plugins in IPlug.

I'm actually working on a new version of IPlug that is based on VST3 and is lock-free. Also the graphics can be done using whatever framework you like (IGraphics, JUCE, VSTGUI etc)

eltopo's icon

eltopo

3月 02 2014 | 5:19 午後

Sure, I understand nothing, but how Gen passing parameters in the cpp code is enumerating and including them in an array of kVstParameter or something. For those who do not know anything, an example would be good, just like the ones you make reference, but using some specific parameter in a dial control and background image.
Anyway I appreciate your contribution and I will continue looking.

Thank you!

thetechnobear's icon

thetechnobear

3月 03 2014 | 4:53 午後

Thanks Oli, that is exactly what I was after :o)

from a quick look, it looks like WDL, is maintained by Cockos (of Reaper fame),
does this means its pretty well maintained?
it looks to have pretty good coverage for guis in the virtual window system...

Im not a beginner in C++, but I do have an aversion to frameworks which enforces there way of working to much, it usually makes then difficult to integrate with other software/frameworks...
(code generators like introjucer I've also sometimes find as much of a hinderance as help, though I've not played with introducer so this may not be the case)

Im also not keen on the juce licensing, I don't want to make commercial software, but that doesn't necessarily mean I want to (or can, due to other frameworks) make my source code open source.

so I guess looks like WDL/IPlug might be a good option for me :)

ollieayre1's icon

ollieayre1

3月 15 2014 | 3:28 午後

Hi Oli

I just tried this and i've found that it compiles your default Flanger plugin fine however as soon as any changes are made to the gen patch and the code is exported again xcode says that the calls in IPLugGen.cpp to: create, destroy, num_inputs, reset and setparameter are ambiguous.

On exporting the code from the edited gen patch it also removes the: #include "gen_export.h" and the namespace gen_export{} lines from the code. I corrected this but it didn't have any effect on the previous problem.

....on a side note as well the duplicate python script also doesn't seem to work properly for the IPLugGen-master folder; it duplicates the folder and changes the name but doesn't alter the names of anything inside. I've used it for duplicating the IplugEffect folder for normal projects and it works in that instance.

Thanks for creating all this though in general, its been great for learning how to build plugins.
Regards
Ollie

ollieayre1's icon

ollieayre1

3月 15 2014 | 6:20 午後

Issue Resolved - I was on Max 6.1.

Updated Max and it works perfectly now.

Great Work again!

jakobglock's icon

jakobglock

8月 08 2014 | 2:46 午後

This is brilliant!

Thanks!

Make Electronic Sound's icon

Make Electronic Sound

9月 07 2014 | 4:08 午後

Thanks very much for this Oli. Truly fantastic resource.

Make Electronic Sound's icon

Make Electronic Sound

12月 03 2014 | 9:47 午後

Hi.

I would like to be able to get multiple channels of audio into a Gen patch but I am stuck.

I have adapted Oli's example patch 'IPlugGen' to have a very simple 2 in 2 out patch with a mix object to control the 2 sources. Currently there is no second source. and ATM, I am just trying to silence one input using inputs[1] = NULL; in ProcessDoubleReplacing. I assumed this would make in2 silent and in1 pass into Gen unchanged but it actually results in total silence.

My question is this: What code should I place in ProcessDoubleReplacing to route audio to in1 OR in2? For example, the following is from the IPlugResampler ProcessDoubleReplacing. How could I send this output to in1 or in2?

  double* out1 = outputs[0];
  double* out2 = outputs[1];

  WDL_ResampleSample *resampledAudio=NULL;
  int numSamples = mResampler.ResamplePrepare(nFrames,1,&resampledAudio);
  
  for (int s = 0; s < numSamples; s++) 
  {
    if (mSampleIndx >= 44100) mSampleIndx = 0;
    resampledAudio[s] = mAudioToResample[mSampleIndx++] * mGain;
  }
    
  if (mResampler.ResampleOut(out1, numSamples, nFrames, 1) != nFrames)
  {
    //failed somehow
    memset(out1, 0 , nFrames * sizeof(double));
  }

  memcpy(out2, out1, nFrames * sizeof(double));

Any help would be great. I've attached the Max Patch since I was not allowed to upload the Xcode Project. If you can take the time to look at this, you'd need to export the code using this patch instead of the example in IPlugGen.

Thanks

Dave

IPlugGenTwoInputs.maxpat
Max Patch
oli larkin's icon

oli larkin

12月 04 2014 | 1:28 午前

not sure i quite understand what stage you're at, but if you want to silence the right channel of the incoming sample buffer in IPlug's ProcessDoubleReplacing, you could do so with

void IPlugGen::ProcessDoubleReplacing(double** inputs, double** outputs, int nFrames)
{
  // Mutex is already locked for us.
  double* in0 = inputs[0]; //pointer to the first sample in the left channel
  double* in1 = inputs[1]; //pointer to the first sample in the right channel
  memset(in1, 0, nFrames * sizeof(double)); // set (nFrames * sizeof(double)) bytes to 0
  perform(gen, inputs, num_inputs(), outputs, num_outputs(), nFrames);
}
Make Electronic Sound's icon

Make Electronic Sound

12月 04 2014 | 7:58 午前

Hi Oli,

Thanks for a speedy reply.

That does indeed silence the right channel and then the one GUI slider controls the blend of left to right. Here's the code that worked:

void IPlugGenTwoInputs::ProcessDoubleReplacing(double** inputs, double** outputs, int nFrames)
{
  // Mutex is already locked for us.
  
  double* in0 = inputs[0]; //pointer to the first sample in the left channel
  double* in1 = inputs[1]; //pointer to the first sample in the right channel
  memset(in1, 0, nFrames * sizeof(double)); // set (nFrames * sizeof(double)) bytes to 0

  perform(gen, inputs, num_inputs(), outputs, num_outputs(), nFrames);
}

I tried your 'IPlugResampler' project and got it working with an array I filled with a one second sample of a wav. It loops and works great. My final goal is to have two such arrays going into Gen so that in my Gen patch I can do x with in1 and do y with in2 etc. Right now the slider would mix between in1 and in2.

So, the next step for me is to get my adaptation of your 'IPlugResampler' project to send it's audio into either in 1 or in 2:

My adaptation of your 'IPlugResampler' project is basically the same except I replace some code in your constructor:

  //Fill the mAudioToResample array with 100 cycles of a 441hz sine wave
  double recip = 1. / 44100.;
  double phase = 0.;
  
  for (int i = 0; i< 44100; i++)
  {
    mAudioToResample[i] = sin( phase * 6.283185307179586);
    phase += recip * 441.;
    if (phase > 1.) phase -= 1.;
  }

With:

const double test[] = {0.862, 0.855957, 0.858887, 0.855316, 0.856354, 0.854279, 0.854156, 0.852905, 0.852234, 0.851349, 0.850403, 0.849701, 0.848724, 0.847931, 0.846893, 0.846436, 0.845734, 0.839447, 0.766388, 0.228851, …….etc etc….

  for (int i = 0; i< 44100; i++)
  {
    mAudioToResample[i] = test[i];
  }

As I said, that works. No problems.

When trying to get the array into Gen I tired this but it failed:

void IPlugGenTwoInputs::ProcessDoubleReplacing(double** inputs, double** outputs, int nFrames)
{
  // Mutex is already locked for us.
  
  double* in0 = inputs[0]; //pointer to the first sample in the left channel
  double* in1 = inputs[1]; //pointer to the first sample in the right channel
  
  WDL_ResampleSample *resampledAudio=NULL;
  int numSamples = mResampler.ResamplePrepare(nFrames,1,&resampledAudio);
  
  for (int s = 0; s < numSamples; s++)
  {
    if (mSampleIndx >= 44100) mSampleIndx = 0;
    resampledAudio[s] = mAudioToResample[mSampleIndx++] * mGain;
  }
  
  if (mResampler.ResampleOut(in1, numSamples, nFrames, 1) != nFrames)
  {
    //failed somehow
    memset(in1, 0 , nFrames * sizeof(double));
  }
  
  memcpy(in1, in0, nFrames * sizeof(double));
  
  //end Resampler example

  perform(gen, inputs, num_inputs(), outputs, num_outputs(), nFrames);
}

I thought the above would play the array through the Gen patch but I just get silence.

My question: How can I send one array of sample data (a loop) to one Gen input and a second array of different sample data (another loop) to the second Gen input? Is this possible? Any help would be great.

If you have the time, I uploaded the project here:

Cheers

Dave

oli larkin's icon

oli larkin

12月 04 2014 | 4:31 午後

bit busy to look at this... i do consultancy via skype if you are still stuck

Make Electronic Sound's icon

Make Electronic Sound

12月 04 2014 | 4:45 午後

Sure. I am still stuck. What's your Skype name Oli?

I'm cambridgekoala (based in Estonia) --> Vladimir Putin cuddling a Koala is my profile picture!

oli larkin's icon

oli larkin

12月 04 2014 | 8:20 午後

well i'm feeling generous... since the C++ looks absolutely horrific on this forum, i've made a gist:

not tested but should show you roughly how to get two separate resampled audio sources into the left and right inputs of your gen process

Make Electronic Sound's icon

Make Electronic Sound

12月 04 2014 | 8:50 午後

Thanks very much Oli, I'll check that out tomorrow.

btw, nice work on Virtual CZ. Picked one up from Plugin Boutique last weekend. Not had much of a chance to play but it's sounding good.

Add me to Skype please. I'm sure I'll need your consultancy soon anyway

Dave

bruno's icon

bruno

6月 07 2017 | 6:13 午前

Hello Oli,

I noticed that inside the Reset() method of IPlugGen you wrote :

gen->sr = GetSampleRate();

reset(gen);

Is it safe to include also (after the gen->sr assign):

gen->vs = GetBlockSize(); //to assign the "buffer size" to "gen vector size"

You did't add it for a specific reason (stability ?), or you simply forgot to add it?

Is it safe to add that assignment at reset() ?

...Otherwise "gen vector size" will be always fixed to " DEFAULT_BLOCK_SIZE " (assigned in constructor)

Thank you in advance for any hint !