PDA

View Full Version : IPlug - graphics question(s)


captain caveman
01-08-2011, 03:26 PM
Hi

I am updating the GUI like this....

//in here
MyPlugin::ProcessMidiMsg(IMidiMsg* pMsg)

//using this
GetGUI()->SetParameterFromPlug(kTransposeAmount,(double) mnTransposeAmount, false);
....which is mostly fine but:

1. The GUI updates, but the non-GUI slider doesn't - is this "fixable"?
2. The GUI only updates when MIDI is received because that is when this class method is called.

I want to update the GUI more regularly.

I can't do it in OnParamChange because the parameters aren't getting changed in that plugin (another plugin is controlling the value).

I could always do it in ProcessDoubleReplacing, but it seems wasteful evaluating an if statement 96000 times a second to see if the value has changed.

Is there anywhere else I can put GUI-updating stuff?

Thanks for any help.

Tale
01-09-2011, 02:29 AM
1. The GUI updates, but the non-GUI slider doesn't - is this "fixable"?
Probably not in REAPER, but maybe in other hosts, see this thread.

I could always do it in ProcessDoubleReplacing, but it seems wasteful evaluating an if statement 96000 times a second to see if the value has changed.

ProcessDoubleReplacing() is not called 96000 times a second @ 96kHz, but rather 96000 times divided by the (variable) buffer size. So you could do it there, it wouldn't be that wasteful.

captain caveman
01-09-2011, 05:39 AM
Okay, that sounds a bit better. I'll have a poke around in a couple of other hosts to see what happens - btw your link doesn't work so I can't read that thread.

Cheers man.

Tale
01-09-2011, 05:47 AM
Sorry about that, I meant this thread.

captain caveman
01-09-2011, 05:32 PM
That's a bummer, I don't know why that got put down as a "live with it" since it's tied in with plugin compatibility/functionality in Reaper.

captain caveman
01-11-2011, 07:07 PM
Back to the original GUI topic, from IGraphics.h.....
// For setting a control that does not have a parameter associated with it.
void SetControlFromPlug(int controlIdx, double normalizedValue);

.... how do I set a control with no parameter associated with it? I have 3 dummy parameters in my code at the moment and I'd like to utilize this so that users of hosts that work correctly with non-GUI plugins don't see them.

Finally, I am going to create an about screen the size of the GUI to come up when a logo is clicked. How do I get a clickable URL/any html into this setup?

Thanks for any help.

Tale
01-12-2011, 02:15 AM
// For setting a control that does not have a parameter associated with it.
void SetControlFromPlug(int controlIdx, double normalizedValue);

.... how do I set a control with no parameter associated with it?
When you create the controls they get a control index number (zero based), regardless of whether they are associated with a parameter. So if you want to set the 3rd control you have created, you can do:

const int MyControlId = 2; // 3rd control
SetControlFromPlug(MyControlId, 0.5);

Or you could save the index number in a member variable on creation of the control:

IBitmap bitmap = pGraphics->LoadIBitmap(FOOBAR_ID, FOOBAR_PNG);
IControl* pControl = new IBitmapControl(this, 100, 50, -1, &bitmap);
mMyControlIndex = pGraphics->AttachControl(pControl);

...

SetControlFromPlug(mMyControlIndex, 0.5);

Finally, I am going to create an about screen the size of the GUI to come up when a logo is clicked. How do I get a clickable URL/any html into this setup?
There is a URL control, which links a rectangle of your GUI to a URL:

pGraphics->AttachControl(new IURLControl(this, &IRECT(x1, y1, x2, y2), "http://www.martinic.com/"));

captain caveman
01-12-2011, 08:34 AM
Thanks very much Tale, you are very kind.

If I could bug you about one last graphics question, how do I call these functions in IControl since they don't have the parameter ID in the arguments for them?
void Clamp(double lo, double hi) { mClampLo = lo; mClampHi = hi; }
bool IsGrayed() { return mGrayed; }

Tale
01-12-2011, 08:56 AM
If you already have a pointer to the GUI in pGraphics, then you can access the controls through that pointer:

const int ctrlIdx = 2;
IControl* control = pGraphics->GetControl(ctrlIdx);
control->Clamp(0.1, 0.9);

The same, but as a one-liner:

pGraphics->GetControl(2)->Clamp(0.1, 0.9);

If you are somewhere in the plug-in where you don't already have a pointer to the GUI, you first need to fetch that pointer:

IGraphics* pGraphics = GetGUI();

All-in-one-liner:

GetGUI()->GetControl(2)->Clamp(0.1, 0.9);

captain caveman
01-12-2011, 07:43 PM
Thanks to that explanation, advanced pointer syntax is now beginning to become more of an intriguing mystery instead of a nightmare. :)

Much obliged!

captain caveman
01-12-2011, 09:38 PM
Btw, is there anything else I need to do to get the plugin to update the parameter than this?
GetGUI()->SetParameterFromPlug(kMIDIControlRootNote,(double) nMIDIControlRootNote, false);
InformHostOfParamChange(kMIDIControlRootNote, nMIDIControlRootNote);
That is from the midi learn mode of my plugin. The GUI is updating (with wrong note names at the moment but that is (hopefully not) another issue), but right-clicking on the parameter in the GUI shows the original value.

Is there anything else I need to do to tell the plugin that a parameter has changed?

Tale
01-13-2011, 12:12 AM
Is there anything else I need to do to tell the plugin that a parameter has changed?
No, that should be all.

BTW, InformHostOfParamChange() expects a normalized value (a double in the range 0.0..1.0).

captain caveman
01-13-2011, 11:01 AM
Thanks, I didn't spot the normalised thing with the InformHostOfParamChange(). Am I right in sating that this is only for host automation though so the plugin shouldn't mind whether or not that is called?

I am struggling with something strange happening when updating a parameter at the moment. I have a MIDI learn button, and I evaluate it's position using an enum switch....
ELearn enLearn = (ELearn) int(GetParam(kLearn)->Value());
int nIsLearnOn = 0;

switch (enLearn) {
case kLOff:
nIsLearnOn = 40;

break;

case kLOn:
nIsLearnOn = 180;
mbLearnMode = true;

break;
}
.... and then turning off the learn mode at the end of the bit that sets the values (in an "if nIsLearnOn == 180" section (the 40 and 180 numbers are not normal, I know, but I am trying to make it more unbreakable than true/false because of the issues I am solving/creating).....
// turn learn mode off after receiving 2nd note off
if ((mbLearnedOne) && (bDone == false) && (mnNote == mnLastLearnedNote) && ((pMsg->Velocity() == 0) || (mEventType == 8)))
{
if (GetGUI()){GetGUI()->SetParameterFromPlug(kLearn,(double) kLOff, true);}
InformHostOfParamChange(kLearn, kLOff);
mbLearnMode = false;
bSendMIDI = false;
mbLearnedOne = false;
mnLastLearnedNote = -1;
nIsLearnOn = 0;
}

What should happen (I think) is that the SetParameterFromPlug turns the Learn parameter off, updates the GUI and therefore the next time the enum switch evaluates the Learn parameter it should be Off and therefore the plugin is not in Learn mode.

What actually happens is the GUI button is switched to the off position, but the plugin still thinks that Learn mode is on until pressing Learn mode on and then off again.

Here's a link to the plug to see the result if anyone's interested....

http://stash.reaper.fm/v/7562/Transpose.dll

.... you can also see the wonky note names I am getting with the Learn function which is weird because I am using a 128 image .png for the note names and just getting the note like this....
nNote = pMsg->mData1;
mnNote = nNote;
... and storing it and updating the GUI like this....
nTransposeRangeLo = mnNote;
mnLastLearnedNote = mnNote;
if (GetGUI()){GetGUI()->SetParameterFromPlug(kTransposeRangeLo,(double) nTransposeRangeLo, false);}
.... by now hopefully someone is doing a face-palm on my behalf because of some school-boy error. :)

Any ideas of what I am doing wrong?

Tale
01-13-2011, 11:43 AM
Am I right in sating that this is only for host automation though so the plugin shouldn't mind whether or not that is called?
Yes.

What should happen (I think) is that the SetParameterFromPlug turns the Learn parameter off, updates the GUI and therefore the next time the enum switch evaluates the Learn parameter it should be Off and therefore the plugin is not in Learn mode.

What actually happens is the GUI button is switched to the off position, but the plugin still thinks that Learn mode is on until pressing Learn mode on and then off again.
GetGUI()->SetParamFromPlug() only updates the control that is linked to the parameter index number in the GUI, but you will have to update your plug-in's internals yourself. A simple and consistent way of doing this is to manually call OnParamChange(). Here is a code snippet that sets a parameter to the value of a MIDI CC message:

double normalizedValue = (double)pMsg->mData2 / 127.;
// Set parameter
GetParam(paramIdx)->SetNormalized(normalizedValue);
// Update internals
OnParamChange(paramIdx);
// Update GUI
GetGUI()->SetParameterFromPlug(paramIdx, normalizedValue, true);
// Inform host
InformHostOfParamChange(paramIdx, normalizedValue);

Any ideas of what I am doing wrong?
The code you have posted looks correct to me... Perhaps the error is in plug-in's constructor (parameter init, or when attaching GUI control)? I would expect something like this to work:

GetParam(kTransposeRangeLo)->InitInt("Foo", 0, 0, 127);
...
IBitmap bitmap = pGraphics->LoadIBitmap(FOO_ID, FOO_PNG, 128);
pGraphics->AttachControl(new IBitmapControl(this, x, y, kTransposeRangeLo, &bitmap));

captain caveman
01-13-2011, 04:11 PM
Wow, thanks sooo much for that! I have spent a lot of time being confused over this.

I've put that code into a class member and everything is working perfectly now when I call it after a parameter change in the code. :)

The solution to the other issue is embarrassingly embarrassing......

I *cough* had an extra octave and *cough* no *cough* Es in any of them. There were 128 notes, but not in any format that anyone uses!!! :)

Top man!

captain caveman
01-14-2011, 09:44 AM
Hi

Just two final, final questions if I might be so bold.....

How do I get the mouse to change to the URL pointy-finger when it is over the area that will trigger it? How do I "hide" this URL-control?

How do I show and hide a just a bitmap (and hide with a mouseclick) and how would I trigger a bitmap to show on mouseover?

Thanks, I (almost) promise those are the final questions and I greatly appreciate this help.

Tale
01-14-2011, 10:15 AM
How do I get the mouse to change to the URL pointy-finger when it is over the area that will trigger it?
I don't realy know. There is the SetCursor (http://msdn.microsoft.com/en-us/library/ms648393(v=vs.85).aspx) Windows function, but I don't know how well it will work with IPlug.

How do I "hide" this URL-control?
As with any control you can do:
GetGUI->GetControl(ctrlIdx)->Hide(true);
// Or:
GetGUI->HideControl(ctrlIdx);

How do I show and hide a just a bitmap (and hide with a mouseclick) and how would I trigger a bitmap to show on mouseover?
You could use a "customized" bitmap control for that (i.e. a class derrived from IBitmapControl), e.g.:
class IHideAndSeekControl: public IBitmapControl
{
public:
IHideAndSeekControl(IPlugBase* pPlug, int x, int y, int paramIdx, IBitmap* pBitmap): IBitmapControl(pPlug, x, y, paramIdx, pBitmap)
{
// Hide on construction
Hide(true);
}

~IHideAndSeekControl() {}

virtual void OnMouseOver(int x, int y, IMouseMod* pMod)
{
Hide(false); // Unhide
}

virtual void OnMouseDown(int x, int y, IMouseMod* pMod)
{
if (!pMod->R) // Ignore right click
{
Hide(true);
}
}
};

For efficiency mouseovers/mouseouts are ignored by default, so to enable them you need to call pGraphics->HandleMouseOver(true) when creating the GUI.

Thanks, I (almost) promise those are the final questions and I greatly appreciate this help.
Yeah, well... We will just see how much your promise is worth. :D :D

captain caveman
01-14-2011, 10:35 AM
Well, it was an "almost" promise. :) This time I do solemnly promise (with my fingers firmly crossed).

One almost nearly final question, well not really a proper question, more of a "request for clarification"....

Where do I put that class?

Thanks again.

Tale
01-14-2011, 02:07 PM
You just put that class definition in the plug-in's .h file. You can also give it its own .h file if you plan on re-using it in other plug-ins.

captain caveman
01-14-2011, 03:59 PM
Thanks, but it appears that once the controls are hidden, the OnMouseOver and OnMouseDown aren't evaluated. If I reverse the show/hide so that the control is visible when constructed, OnMouseOver works and hides it but OnMouseDown doesn't work. If it is hidden, as per your example, in the constructor then the OnMouseOver doesn't "see" the control at all.

It appears that Hide (and GrayOut, which I tested too) cancel out all the mouse stuff.

Would there be a way around that?

Tale
01-15-2011, 02:33 AM
Yeah, you are right, I wasn't thinking.

How about this: A control that sets its internal mValue to 1 on mouse over, and resets it to 0 on mouse click. Instead of hiding it you only draw it when its value is 1:

virtual bool Draw(IGraphics* pGraphics)
{
if (!mValue)
return true;
else
return IBitmapControl::Draw(pGraphics);
}

I assume you can recode the OnMouseOver() and OnMouseDown() yourself, right?

captain caveman
01-15-2011, 06:42 AM
Thanks, setting the member variables inside the class member is easy enough, as always though I am lost as to how to call that new class member from inside MyPlugin.cpp (if I am understanding you correctly.

I'm sure I'll have a Eureka moment soon with pointers.

Cheers.

Tale
01-15-2011, 01:57 PM
Thanks, setting the member variables inside the class member is easy enough, as always though I am lost as to how to call that new class member from inside MyPlugin.cpp (if I am understanding you correctly.
I am sorry, but now you have lost me... What was the question again?

captain caveman
01-16-2011, 12:36 AM
Yes sorry, maybe I wasn't being clear or was misunderstanding you.

I have updated the class so that all it does is set a member variable to true or false, instead of the Hide stuff. I don't know if this is right, or if it is how to draw anything with....

virtual bool Draw(IGraphics* pGraphics)
{
if (!mValue)
return true;
else
return IBitmapControl::Draw(pGraphics);
}
Thanks for your patience, this is the last hurdle!

Tale
01-16-2011, 01:56 AM
You don't need to draw anything or even call Draw() yourself. You just paste that Draw() code into the control I posted earlier (you know, where the OnMouseOver() and OnMouseDown() are also at). Then C++ will automatically call the new Draw() method for that specific control.

captain caveman
01-16-2011, 04:30 AM
Ah right, hopefully looking at some of the custom controls posted elsewhere in the forum will make more sense now.

Unfortunately this particular example isn't working for me, which at least explains my ongoing confusion. :) Repeatedly opening and closing the GUI of the plugin for some reason shows the image associated with the control, but mouseover doesn't.

Here's the code (I have #included this header file in Transpose.cpp) ...

#ifndef __CUSTOMCONTROLS__
#define __CUSTOMCONTROLS__

// In the project settings, define either VST_API or AU_API.
//#include "IPlug_include_in_plug_hdr.h"
//#include "Transpose.h"


class IHideAndSeekControl: public IBitmapControl
{
public:

bool mDrawLogoControl;

IHideAndSeekControl(IPlugBase* pPlug, int x, int y, int paramIdx, IBitmap* pBitmap): IBitmapControl(pPlug, x, y, paramIdx, pBitmap)
{
// Hide on construction
//Hide(true);
bool mDrawLogoControl = false;
}

~IHideAndSeekControl() {}

virtual void OnMouseOver(int x, int y, IMouseMod* pMod)
{
//Hide(false); // Unhide
mDrawLogoControl = true;
}

virtual void OnMouseDown(int x, int y, IMouseMod* pMod)
{
if (!pMod->R) // Ignore right click
{
//Hide(true);
mDrawLogoControl = false;
}
}

virtual bool Draw(IGraphics* pGraphics)
{
if (!mDrawLogoControl)
return true;
else
return IBitmapControl::Draw(pGraphics);
}



private:

};

#endif
... should that work?

captain caveman
01-16-2011, 07:18 PM
No dice?

I actually want to just have a graphic that appears on mouse-over and disappears when the mouse isn't over so that I can highlight a URL control. This is the last bit of the plugin(s).

Tale
01-17-2011, 01:26 AM
No dice?
Actually no time (until just now).

Anyway, it works after adding SetDirty() to the OnMouse* methods, so IPlug knows it needs to redraw the control:

virtual void OnMouseOver(int x, int y, IMouseMod* pMod)
{
//Hide(false); // Unhide
if (!mDrawLogoControl)
{
mDrawLogoControl = true;
SetDirty();
}
}

virtual void OnMouseDown(int x, int y, IMouseMod* pMod)
{
if (!pMod->R) // Ignore right click
{
//Hide(true);
mDrawLogoControl = false;
SetDirty();
}
}

(This still doesn't really work, because when you click the image away in OnMouseDown you still inside the image, so OnMouseOver immedtiately fires again. The result is that the image disappears and re-appears in a blink of an eye. But if you replace the OnMouseDown with OnMouseOut it should work.)

captain caveman
01-17-2011, 04:01 AM
Thanks very much Tale, that's it working now! I appreciate the help. :)

Tale
01-17-2011, 04:50 AM
Yay! :cool:

captain caveman
02-16-2011, 08:26 AM
*cough*, I'm back. :)

I thought everything was fine, but when I declare the a URL control and the HideAndSeek control in the same space in the GUI, only whichever one was declared first works.

Is it possible to have an OnMouseDown() within the custom control that goes to a URL (ideal) or is there any way of having layered controls that both trigger when on top of each other?

Tale
02-16-2011, 08:47 AM
Perhaps you can derive the IHideAndSeekControl from IURLControl instead of from IBitmapControl?

Is it possible to have an OnMouseDown() within the custom control that goes to a URL (ideal)
Sure:

void OnMouseDown(int x, int y, IMouseMod* pMod)
{
mPlug->GetGUI()->OpenURL("http://www.martinic.com/");
}[/code]

captain caveman
02-16-2011, 08:54 AM
Thanks Tale, that's it linking nicely now! Good man!

Tale
02-16-2011, 08:59 AM
That was quick! :D

captain caveman
02-16-2011, 10:38 AM
Well, all I have to do was to quickly wipe the blood of the screen, bandage my head up and type the code you posted. :D

captain caveman
02-22-2011, 05:58 AM
Sorry, sorry, sorry, I have one more question about this custom control. :)

At the moment I am putting my logo in the background image and using this to highlight it by overlaying it (off by a pixel or two) on MouseOver.

Would it be easy enough to have the Custom Control work with a 2 image multi image so that it shows the first image until MouseOver, where it cycles to the 2nd? This would make placement a lot easier, having it all in one place.

Cheers

Tale
02-22-2011, 12:32 PM
Yes, that sure is possible. You would need to adapt the control's Draw() method, so that it always draws the 1st bitmap, but the 2nd bitmap only on mouse over. BTW, instead of using two bitmaps, it could also be done with one stacked bitmap.

captain caveman
02-22-2011, 02:14 PM
Thanks, I had a look at IControl.cpp to see what I'd need to include in the custom control and see from ISwitchControl::OnMouseDown() that it is adjusting mValue between a range of 0 and 1 to change the bitmap image from a stacked bitmap.

So I did this...

virtual bool Draw(IGraphics* pGraphics)
{
if (!mDrawLogoControl)
{
mValue = 0;
SetDirty();
return IBitmapControl::Draw(pGraphics);
}
else
{
if (mBitmap.N > 1){mValue = 1;}
else {mValue = 0;}

SetDirty();
return IBitmapControl::Draw(pGraphics);
}
}

... but it doesn't change the image from the first to the second, so unfortunately it's not (just) the mValue that needs changing (I checked with just mValue = 1 instead of the if statement too).

What am I missing?

Tale
02-22-2011, 02:33 PM
Have you specified the number of stacked bitmaps when you loaded the bitmap (in the plug-in's constructor)? By default a bitmap is loaded non-stacked:

IBitmap bitmap;

// Single-frame (non-stacked) bitmap
bitmap = pGraphics->LoadIBitmap(SINGLE_BITMAP_ID, SINGLE_BITMAP_PNG);
// Or:
bitmap = pGraphics->LoadIBitmap(SINGLE_BITMAP_ID, SINGLE_BITMAP_PNG, 1);

// Two-frame stacked bitmap
bitmap = pGraphics->LoadIBitmap(STACKED_BITMAP_ID, STACKED_BITMAP_PNG, 2);

captain caveman
02-22-2011, 02:41 PM
I just assumed that what I was doing was wrong so didn't think of checking everything else. Stupidly, I had commented out pGraphics->HandleMouseOver(true) so obviously the MouseOver code wasn't working so the mDrawLogoControl was never true.

Thanks for confirming I was on the right track and it's working as planned! :)

Tale
02-22-2011, 03:14 PM
Cool! :cool:

captain caveman
03-18-2011, 11:37 AM
Hi, I am wondering whether anyone has any ideas of how to implement a double-pot EQ type control?