vstgui4.0 creating a DrawContext to draw primitives

DSP, Plugin and Host development discussion.
RELATED
PRODUCTS

Post

Ah, how cool! 8-)
I got the BezierCurves to work, too. That was another interesting adventure and another great reminder to brush up on my C++.
The drawGraphicsPath function had a filled path as default and I wouldn't have dreamed it was necessary or even possible to write something like this to set the mode: context->drawGraphicsPath(cgp, CDrawContext::kPathStroked);
Hahaha, the things that fascinate me now, it'll be so ridiculous to look back at this excitement of mine, probably already in only a few days.

Anyway, this is fun! :hyper:

Again, BT, you're a massive help and you're AWESOME! :tu:

Post

Today's log entry goes:
Loads of success with beautiful curve drawing (anti aliased) and elegant handle handling.

Trying to figure out how to make my custom control fully functional so that it reports properly for valueChanged. I'm afraid I might have to turn my CView into a full on CControl for that matter, but it seems to require a deep exploration. I will use CKnob and study CControl to track down what I have to do. :shrug:

Other than that: Wow, this is already looking quite pretty! Still expecting plenty of frustration ahead, but at least it holds clear and glorious promise! :)

Post

My Brains are on fire! :shock:
...I'm trying to pursue all the steps necessary to fiddle in my controllistener into the valuechanged logic and we're back to frustration, haha... crap!
Maybe I'm just too tired, I don't know. I'll keep at it.

Post

I keep wildly asking question, but...

Is there a regular or official way to have an x/y controller in vstgui 4.0 for vst2.4?

Post

BEHOLD!!! For I have figured out a perfect solution!!! :hail: me! :lol:

No, honestly, it's awesome and so simple and totally regular, really; No Hack!

I've made a raw CControl based class that does nothing but act as hub from my envelope gadget to the main AEffGUIEditor. (...although I called it "dummy" it really still is no hack, seriously.. :scared: )

dummy.h

Code: Select all

class dummy : public CControl
{
public:
	dummy(const CRect& size, CControlListener* listener, int32_t tag);
	dummy(const dummy& dumb);

	virtual void draw(CDrawContext* pContext);

	CLASS_METHODS(dummy, CControl)
protected:
	~dummy();
private:
};
dummy.cpp

Code: Select all


dummy::dummy(const CRect& size, CControlListener* listener, int32_t tag)
: CControl(size, listener, tag)
{
	setWantsFocus(true);
}

//------------------------------------------------------------------------
dummy::dummy(const dummy& v)
: CControl(v)
{}

//------------------------------------------------------------------------
dummy::~dummy()
{}
//------------------------------------------------------------------------
void dummy::draw(CDrawContext *context)
{
	setDirty(false);
}

Inside my curve gadget class I've assigned two controller pointers:
CControl *cx;
CControl *cy;

I gave MyEnvelope (inherited CView) the following arguments:

Code: Select all

	MyEnvelope(const CRect& size, CControlListener* listener, int32_t tag, int32_t tag2) : CView(size)
	{ 
		cx = new dummy(CRect(0, 0, 10, 10), listener, tag);
		cy = new dummy(CRect(0, 0, 10, 10), listener, tag2);
	}
In the AEffGUIEditor I declared those controls like one would a knob or a slider...

Code: Select all

	env = new MyEnvelope(CRect(300, 100, 500, 200), this, curveHandle01x, curveHandle01y);
	newFrame->addView(env);

	controls[curveHandle01x] = env->cx;
	controls[curveHandle01y] = env->cy;
In the valueChanged and SetParameter functions it's then totally straight forward...

Code: Select all

void TutorialEditor::valueChanged(CControl* pControl)
{
	effect->setParameterAutomated(pControl->getTag(), pControl->getValue());
}

void TutorialEditor::setParameter (VstInt32 index, float value)
{
	if (frame && index < kNumParameters)
	{
		controls[index]->setValue(value);
		if (index == curveHandle01x)
		{
			env->setX(value);
		}
		if (index == curveHandle01y)
		{
			env->setY(value);
		}
	}
}
The setX and setY functions inside MyEnvelope do the remapping of the 0.0-1.0 floats, while I do the reverse inside the MouseMoved function to pass proper 0.0-1.0 floats back to the listener.

That's all... super easy, once I understood the whole abstract class concept of C++ that somehow puzzled me at the time. It's hard to be a noob, that's for sure. But then, it's also exciting, including the frustration. It's easy to predict that I'll flip out again very soon, hahaha, but- really- the more exhausting troubles get, the more beautiful, sweet and mildly euphoric feels the solution! :phew: :wheee: :hyper:

I'm sure that all of the above is still pretty dirty, because I am just beginning to understand things and assume to make much smarter (or more informed) choices later. For now, though: YEHAWWW!

Post

Uh, that's been a while again. I've gained a good deal of control over all the gui stuff and feel a lot more confident.
I've spent the last 3 days writing the real time synthesis (no wavetables) with my very own filter experiment, which seems to do the trick quite well.

Right now I'm going back to gui and program parameter matters. I've had a "dumb" question, but I think I have figured out a solution.

However, the day is coming MUCH closer that I can share with you my first little vst. I will only compile Windows versions for now (64bit and maybe 32bit, if somebody really wants that?!). Later on, if all goes well, I might have a go at Virtualbox to try some xcode compiles. We shall see...

Post

A really, really DUMB question, but forgive me, please! I'm just so very tired after researching and "successfully" implementing chunks instead of single parameters... my brains are fried! :bang:

Hmmm...anybody know TAL noisemaker. Yes, yes, I just found the source and was overly excited just to find out it's made with JUCE and damn near useless to me. My head is spinning as it is... :dog:

Anyhow, just to explain what I'm after:
TAL noisemaker has an bezier based envelope, but it clearly stores the knots in a way that the DAW won't see or display as parameters. (Mulab offers a "generic editor" to edit all parameters and the knots, which are dynamic, won't show as parameters. Exactly what I want, too!)

I don't know how I can bypass the host's "eyes" and store, fetch and probably update "custom" parameters from my gui element?!

Any hints? :help:

Post

You create just the GUI widget (CView/CControl/CKnob?), and on the callback you do whatever you want - link it to a value in the processor etc. Just bypass the whole setParameter() setup.

Post

Thanks, Mayae, but the link to the "processor" (main effect?) is where I'm still too stupid... not entire sure what I'm doing there, yet. Many things still puzzle me.
I've got a CView widget right now, by the way. Couldn't get a direct connection to my parameters, yet. Crap, I'm so ashamed of my own ignorance.

God, I feel so idiotic, haha...I'm so sorry. Maybe I'm just too tired, but I'm really frustrating myself right now.

I found one curious little issue that I resolved, having forgotten to include the synth's header to my gui header, allowing me to directly set parameters coming from the host into my CView widget. But I still don't know how or where best I would get my current program's parameters. I figured I'd use "valueChanged" to locate my data, but I simply don't know how. :shrug: ...or should I say: I don't know how far I have to go to get it or if there is a simple regular way?! :scared:

Post

Just to clear a few things up: The processor, yes, is the main effect. It's created when you spawn the plugin and is the basis of the communication between the host/daw and your program, and defines the lifetime of your program. The processor stores all 'parameters' as well, which basically is variables the processor exposes to the host.

This is pretty much where the basic VST specification extends to. The rest (GUI/widgets/shared data) is volatile and may or may not exist, and should not be depended on for processing.

You usually create a set of widgets to allow the user to control the parameters you expose to the host, but you don't have to. On the other hand, you can create a set of widgets (which is just that - visuals, that have nothing to do with the VST and/or processing) that controls 'private' parameters. Private being - whatever, even nothing.

All user widgets usually have some sort of callback that indicates the value changed. Normally you override this (I can't remember the specific mechanism in VSTGUI) to alter the state of the parameter you share with your host, but you can really do whatever you want.

I hope I didn't include too much basic knowledge, however it seems you're maybe missing a link here somewhere :) Just remember, the GUI has nothing to do with parameters basically, you use the GUI to control the parameters but it is in no way a direct relationship - hence you can add visual controls/parameters without publishing them to the host.

As a side note... JUCE really is a lot easier in a lot of ways, and you should definitely consider it.

As for your last part, I'm not really sure I understand your intent - usually your 'data' is a set of variables inside your processor class - which your GUI has a reference to?

Post

Uh, Mayae, thank you so much for writing all this, that's super nice! It really doesn't hurt to go all the way down. It gives me a great feeling to read all of this so lightly already. I do understand those relationships in principle, but my problem might be that I haven't entirely worked my way into it, yet.

Let me explain a little more where I'm at. I'm using the valueChanged callback already, of course, to let the processor grab my "standard" parameters. While I even make a distinction between those parameters that are meant to be automated and those, which are not. (setParameterAutomated() and just setParameter()).
The trouble I'm having is that I want to attach parameters (data) to my programs that are not meant to be "seen" by the host, like dynamically allocated spline knots, for example. I just don't quite understand how I can have the processor communicate with the GUI outside of the normal setParameter and valueChanged pipeline.
In my naive mind, I would've let my GUI widget pass data sets directly into my program's parameter chunk, either during mouseMove or at mouseUP, you know. Then I would've tried to figure out how to inform the host to store the chunk...probably calling valueChanged, I guess.
Currently I've actually arranged for my CView to contain a set of CControls that are being tagged like ordinary controls would be. That's kind of cool, actually, but just one more of my wicked ways to explore possibilities.

If you know TAL noisemaker, as the simplest example, how do you think it deals with the curve knots?

But just once again: Thank you already and again for taking the time to help me! That's really super sweet! :)

OH, and I'm afraid of JUCE or any other "middleman", besides vstgui, actually... I almost wished I'd know enough to get around that, too, haha. I really want to understand and control things from as deeply within as possible or reasonable. But it's a rocky road for sure. :?

Post

Ah, okay I see where you're at. I must admit I never really dealt with programs, because, as you found out, getting support for non-parameter state data requires some extra work. To be honest, so far I haven't created anything that actually made use of programs, so I might not be the best person to ask.

Instead I went straight for 'chunks' as they are called in the VST spec. It seems you've already dealt with them, so you probably know what they are. But basically, it is just a raw memory block you use to serialize all your data, and your host handles this on project/program recall/save. If you still want to use programs, I see there's a function called AudioEffect::programsAreChunks(bool) which you can toggle such that the host emulates program set/get through getChunk()/setChunk(). I think that would work, but I haven't tried it.

In general, the processor should not try to communicate with the GUI - only the other way around. This is obviously not possible if the processor somehow changes parameters (through automation, LFO's etc). This is where it also gets fun, because you're not allowed to interact with the GUI in general in process() callbacks, because it can involve non-deterministic code (such as memory allocation and/or multi threading locks). Usually, people either tend to set a flag in the processor the GUI checks regularly and updates itself if set, or create a system involving lock-free data queues.

I've created a simle GUI/processor boilerplate-code example here, just to let you know how I would arrange something like that. It is to be considered pseudo-code because I haven't used VST/VSTGUI in ages.

example processor: http://pastebin.com/n4pGsVvV
example gui: http://pastebin.com/ydi9Z5Fa

Notice the Data::curve variable is not a 'parameter' unlike the others, however there exists a control for it in the GUI, and it is saved through getChunk()/setChunk(), and it will probably be used in the process() call. You have to program the GUI to correctly set this data independently of the VST spec (and beware of multi threading). You can of course extend this to any functionality, since data variables and gui widgets can be created dynamically.

If the TAL noisemaker is built on JUCE, it would use a similar system to what I posted.

Post

AHHH, you're so great, Mayae! That's fantastic!!! :tu:
After your earlier support I started to switch my thinking around already, but digressed with some more dsp fun to lighten my mood, haha.
Now, I see immediately where you're going with this and I'll investigate closely!
It makes perfect sense to rather completely enslave the gui and make sure it won't have any crucial meaning to the processor other than interface (duh)...I've already seen what happens, if people don't deal with it correctly (Or2v - totally messing up without the GUI being active in some DAWs).

Anyway, I'm excited and super grateful! I'll have to run some chores now, but can't wait to resume!!! :hyper:

You rock! 8-)

Post

Yeah just store whatever data you want in chunks.

Post

Mayae, it doesn't seem to work the way you've suggested, but I might do something utterly wrong.
I love your idea, just adding a method to the AudioEffectX (processor), but it acts as though I can't add any methods or my gui can only see the template methods?!

Main...

Code: Select all

class mySynth : public AudioEffectX
{
public:
	mySynth (audioMasterCallback audioMaster);
	~mySynth ();

	void setCurve(float *in)
	{
		mySynthProgram * ap = &(bank.programs[curProgram]);
		ap->test[0] = in[0];
		ap->test[1] = in[1];
		ap->test[2] = in[2];
		ap->test[3] = in[3];
	}
...
GUI...

Code: Select all

void myGui::valueChanged(CControl* pControl)
{
	if (pControl->getTag() < ktest0)
		effect->setParameterAutomated(pControl->getTag(), pControl->getValue());
	else
	{
		effect->setParameter(pControl->getTag(), pControl->getValue());
		effect->setCurve(mySpline->values); //error: setCurve is not a member of 'AudioEffect'
	}
}
I'm sure it's something silly, but well... I'm puzzled. :shrug:
I do see that you're doing a few different things I haven't done, yet. Namely the "processor" stuff I haven't seen, yet. Hmmm... I keep investigating. I hope, it's not a weird discrepancy between sdk versions?! :neutral:

In the constructor for the "processor" I'm using I have, for example:

Code: Select all

	extern AEffGUIEditor* createEditor(AudioEffectX*);
	setEditor(createEditor(this));
Oh, that's ridiculous...I've just noticed some more of the stupidity of taking over existing code without truly having studied it, yet... pfff... :roll:

MentalState* proclaim(Intelligence* state)
{
return new totallyRetarded(state);
}
Man, I feel so reveal(proclaim(this));
Last edited by Taron on Wed Sep 30, 2015 9:00 pm, edited 1 time in total.

Post Reply

Return to “DSP and Plugin Development”