COCKOS
CONFEDERATED FORUMS
Cockos : REAPER : NINJAM : Forums
Forum Home : Register : FAQ : Members List : Search :

Go Back   Cockos Incorporated Forums > Other Software Discussion > WDL users forum

Reply
 
Thread Tools Display Modes
Old 01-11-2017, 12:49 PM   #1
Bobflip
Human being with feelings
 
Join Date: Nov 2016
Posts: 341
Default Correct usage of pointers when sending data to an IControl with SetValueFromPlug

Hi again, making good progress with my experiments so far! Hit a bit of a problem where I'm trying to pass an array of all the samples in a ProcessReplacing thread to a custom visual control based on IControl.

I currently have this:


in MyPlug.h

Code:
private:
	int mMyControl;

in MyPlug.cpp


Code:
 	mMyControl = pGraphics->AttachControl(new MyControl(this, IRECT(10, 10, 500, 110)));


void MyPlug::ProcessDoubleReplacing(double** inputs, double** outputs, int nFrames)
{
	for (int s = 0; s < nFrames; ++s, ++in1, ++in2, ++out1, ++out2)
	{
		*out1 = *in1;
		*out2 = *in2;

		sample = *in1;

		mMyControl->SetValueFromPlug(sample);
	}
But this gives me the Error C2227 left of '->SetValueFromPlug' must point to class/struct/union/generic type.


It passes a single sample fine in this bit

Code:
	if (GetGUI())
	{
		GetGUI()->SetControlFromPlug(mMyControl, sample);
	}
But I'm sure that I shouldn't be calling GetGUI() for every sample! I have seen a lot of posts about how I need a pointer to the control, but for all my reading I'm still a bit confused about when and how to use them in this kind of situation. Also, are there other ways I should be achieving this?
Bobflip is offline   Reply With Quote
Old 01-11-2017, 03:33 PM   #2
earlevel
Human being with feelings
 
Join Date: Dec 2015
Posts: 331
Default

Quote:
Originally Posted by Bobflip View Post
Hi again, making good progress with my experiments so far! Hit a bit of a problem where I'm trying to pass an array of all the samples in a ProcessReplacing thread to a custom visual control based on IControl.

I currently have this:

in MyPlug.h

Code:
private:
	int mMyControl;
in MyPlug.cpp

Code:
 	mMyControl = pGraphics->AttachControl(new MyControl(this, IRECT(10, 10, 500, 110)));

void MyPlug::ProcessDoubleReplacing(double** inputs, double** outputs, int nFrames)
{
	for (int s = 0; s < nFrames; ++s, ++in1, ++in2, ++out1, ++out2)
	{
		*out1 = *in1;
		*out2 = *in2;

		sample = *in1;

		mMyControl->SetValueFromPlug(sample);
	}
But this gives me the Error C2227 left of '->SetValueFromPlug' must point to class/struct/union/generic type.

It passes a single sample fine in this bit

Code:
	if (GetGUI())
	{
		GetGUI()->SetControlFromPlug(mMyControl, sample);
	}
But I'm sure that I shouldn't be calling GetGUI() for every sample! I have seen a lot of posts about how I need a pointer to the control, but for all my reading I'm still a bit confused about when and how to use them in this kind of situation. Also, are there other ways I should be achieving this?
To start, Bob, you're declaring (correctly) that mMyControl is an int, a basic data type. Then you are trying to call SetValueFromPlug as if it were a method of a class int.

SetValueFromPlug is a method of class Control. AttachControls, a method of your plugin's IGraphics object. starts with an empty list of controls an gives you back an index of the control you just attached—first call adds the control and gives back 0, second calls adds the control to the list and gives back 1, etc.

If you choose to not save a pointer to the control, you can instead save the index to it, and ask the IGraphics object to hand back a pointer to the control of that index. You do that with IGraphics::GetControl. For instance, if you are in your plugin (IPlug subclass), you can use the GetGui() call to get a pointer to its IGraphics object, then call GetControl to get the control of that index, then use that to get the value.

But there's a bigger conceptual problem. You are trying to call something with side effects in the audio processing thread. The GUI should be messed with in the graphics thread at a lower priority. That is, you probably want to signal to your plugin that you want the update to happen, and continue on with the audio processing. The plugin, probably while idling, should check for the task, and do the work.
earlevel is offline   Reply With Quote
Old 01-11-2017, 08:56 PM   #3
Bobflip
Human being with feelings
 
Join Date: Nov 2016
Posts: 341
Default

Thanks for the explanation, that's certainly made the heirarchy clearer.

The project I'm working on is creating an oscilloscope, so thought that the audio data needed to be passed to the control by the audio processing thread at all times.
All the drawing code is within the custom control's bool Draw () function, and I'm not calling this at any point.


I've since got the data passing working by putting GetGUI()->SetControlFromPlug(myControl, sample) in the s < nFrames loop, but am I right in thinking that using a pointer to mMycontrol in this loop would be more efficient and clean than getting the pointer each time using GetGUI?

What would be the correct syntax for saving and using a pointer to a control? I swear I must be getting the asterisks in the wrong places and not in the others!
Bobflip is offline   Reply With Quote
Old 01-11-2017, 11:01 PM   #4
earlevel
Human being with feelings
 
Join Date: Dec 2015
Posts: 331
Default

Quote:
Originally Posted by Bobflip View Post
Thanks for the explanation, that's certainly made the heirarchy clearer.

The project I'm working on is creating an oscilloscope, so thought that the audio data needed to be passed to the control by the audio processing thread at all times.
All the drawing code is within the custom control's bool Draw () function, and I'm not calling this at any point.

I've since got the data passing working by putting GetGUI()->SetControlFromPlug(myControl, sample) in the s < nFrames loop, but am I right in thinking that using a pointer to mMycontrol in this loop would be more efficient and clean than getting the pointer each time using GetGUI?

What would be the correct syntax for saving and using a pointer to a control? I swear I must be getting the asterisks in the wrong places and not in the others!
There's not a ton of overhead to getting a control pointer from the GUI, but of course it's of course it's more efficient to just keep the control pointer yourself—and maybe it make the code easier to read too. From your original post:

Code:
mMyControl = pGraphics->AttachControl(new MyControl(this, IRECT(10, 10, 500, 110)));
Your control is created by the New command, so you'd want to save it there. Something like:
Code:
IControl *mMyControlPtr;   // in your plugin class declaration
...
pGraphics->AttachControl(mMyControlPtr = new MyControl(this, IRECT(10, 10, 500, 110)));
Of course, instead of the "IControl *", you'd probably use whatever control type you are using that descended from IControl. Then later, you can myControlPtr->SetValueFromPlug. Again I wouldn't do it in the audio processing thread. First, take a look at what can happen in SetDirty, called from SetValueFromPlug. Also, think about what could happen if you are in the middle of those control routines when the audio thread takes over, and you call into those same control routines. What fun bugs might lurk there.
earlevel is offline   Reply With Quote
Old 01-13-2017, 09:28 PM   #5
Bobflip
Human being with feelings
 
Join Date: Nov 2016
Posts: 341
Default

Cheers for the tips, they're appreciated! I have an idea to test and I'm sure I'll be back to this thread with further questions soon, but want to make sure I've amply explored what you've said first. Just wanted to say thanks before that time as well!
Bobflip is offline   Reply With Quote
Old 01-17-2017, 05:56 AM   #6
Bobflip
Human being with feelings
 
Join Date: Nov 2016
Posts: 341
Default

Ok, I'm still having trouble here.
I've taken the wave data buffer to the main class and feel like I've got a better hold of the pointers better, but I still can't work out the correct way to be passing the audio buffer pointer to the control.

Should I be passing an pointer to array of wave data from the main class to the control, or should I be getting the custom control's draw() routine to pull the array from the main class's public variables? If the former, where should it happen instead of processAudioReplacing()? If the latter, what's the correct way to access this buffer from draw()?

The VU meter in the iMultiTargets example appear to pass the sample data if GetGUI() is true, but this is within the audio thread and from what you've said this is apparently not the way to go?

I've been reading and experimenting a lot but now my head's cluttered with lots of things that didn't quite work out!
Bobflip is offline   Reply With Quote
Old 01-17-2017, 06:14 PM   #7
earlevel
Human being with feelings
 
Join Date: Dec 2015
Posts: 331
Default

OK, so you want to update a control based on audio data, right?

You'll probably want crunch the data in the audio processing routine. The audio routine will be getting buffers faster than your UI will be updating, so it doesn't make sense to pass raw data to the control. (Also, the data will be long gone if you're passing a pointer to the audio buffers—you'd have to copy it.)

As a crude example, say you want to keep track of peaks. you might have an "over" indicator for each channel that turns red when peaks are over 0 dB (let's say samples > 1.0). In addition to your usual audio processing, you'd keep a "peak" class variable per channel, and for each sample you'd take the abs of the sample, and if it's greater than the saved peak, store it to the peak variable.

Then the control would just check the plugin's peak variable to see whether to set the indicator red for that channel. You probably let the user click the indicator to clear it, in which case the control would tell the plugin to re-init the peak variable to zero.

I guess that would fall under your "getting the custom control's draw() routine to pull the array from the main class's public variables" choice. In other words, your control would poll info left by the audio processing routine.
earlevel is offline   Reply With Quote
Old 01-18-2017, 06:21 AM   #8
Bobflip
Human being with feelings
 
Join Date: Nov 2016
Posts: 341
Default

Hi again, and thanks again!

Last night I actually managed to get it working again by passing the audio buffer to the control using a custom routine called copyBuffer called from processAudioReplacing with
Code:
if (GetGUI) pMyControl->copyBuffer(AudioBuffer)
For now it was very crude and would copy all values to the control's audio buffer ready for processing in the draw() routine, but as expected this was very taxing on the CPU! Had gone to bed before looking at ways to optimise that though.

The missing piece in the puzzle still remains for me - what's the correct way to poll the main thread's public data from the draw() routine?
Bobflip is offline   Reply With Quote
Old 01-18-2017, 01:12 PM   #9
earlevel
Human being with feelings
 
Join Date: Dec 2015
Posts: 331
Default

Quote:
Originally Posted by Bobflip View Post
Last night I actually managed to get it working again by passing the audio buffer to the control using a custom routine called copyBuffer called from processAudioReplacing with...
As long as you realize that the two will be running at different rates. So, depending on implementation, at minimum the control will skip over buffers of data, or the data will change while the control is processing it.
earlevel is offline   Reply With Quote
Old 01-18-2017, 01:31 PM   #10
Bobflip
Human being with feelings
 
Join Date: Nov 2016
Posts: 341
Default

Well, the approach was a bit of an interim hack while waiting to learn the proper way to do it. As it is I can only pass data to the control from the processAudioReplacing routine. Please can you tell me the correct way to access the main thread's public data from the draw() routine?
Bobflip is offline   Reply With Quote
Old 01-23-2017, 10:51 PM   #11
Bobflip
Human being with feelings
 
Join Date: Nov 2016
Posts: 341
Default

Please could someone help me out here?

I understand that I need to access the audio buffer in the main class's public data, but don't know how I should achieve that from a custom IControl's draw() routine, and not been able to find anything in the examples that makes it clearer for me.
Bobflip is offline   Reply With Quote
Old 01-23-2017, 11:25 PM   #12
earlevel
Human being with feelings
 
Join Date: Dec 2015
Posts: 331
Default

It's tough to give detailed advice without detailed without a detail account of what's needed. And, personally, I wouldn't have the time to go through the detailed account and give a detailed reply

So, I can only give general advice. As I said, I doubt that you need to let the control have access to all the audio—the user interface wouldn't keep up anyway (sure, there's a variety of ways you could do it, but I don't have time to give possibilities that are most likely not the best approach). So, typically you would reduce the data in the audio thread, in ProcessDoubleReplacing. Since it's part of your plugin, and the controls are too, it's not hard to have ProcessDoubleReplacing write to a buffer owned by the plugin (or sent it to an object owned by the plugin). The control would also need to have access (during initialization, the plugin would create the control, and pass it a point to the buffer or management object). Alternatively, the control could do its own data management, the plugin could keep a pointer to the control (or you could look it up by index each pass), and the audio handler could just send the info straight to the control.

I don't know if that helps...
earlevel is offline   Reply With Quote
Old 01-25-2017, 12:51 AM   #13
ynohtna
Human being with feelings
 
Join Date: Jan 2014
Location: Brighton, UK
Posts: 21
Default

Are you familiar with the concept of ring buffers, Bobflip?

They're the common solution for a producer thread (your audio processing) to write data that is then read in a consumer thread (your UI), and can be implemented such that they don't require the use of threading synchronisation primitives.
ynohtna is offline   Reply With Quote
Old 01-27-2017, 08:27 PM   #14
Bobflip
Human being with feelings
 
Join Date: Nov 2016
Posts: 341
Default

Sorry for the delay... recorded a video of what I was doing, started writing a reply, got called away and that was that for a few days!
This is the result I've been working towards, as I particularly want the scrolling and dynamic zooming style.

https://webmshare.com/zWLBb

Earlevel - thanks for the help, in retrospect my question was a bit vague!

Quote:
Originally Posted by earlevel View Post
Since it's part of your plugin, and the controls are too, it's not hard to have ProcessDoubleReplacing write to a buffer owned by the plugin (or sent it to an object owned by the plugin). The control would also need to have access (during initialization, the plugin would create the control, and pass it a point to the buffer or management object).
This is the bit I just can't get right - I simply haven't got the knack of where to put the asterisks in each instance, and the confusion is compounded by asterisks being able to represent arrays as well...
I have set up what I think is a pointer in the initialisation parameters of the control. But during initialisation either I can pass the pointer to the control but not read the data from it in draw(), or I can get draw to treat it as an array but I can't pass it properly during initialisation, or some similar but related error.

Quote:
Originally Posted by earlevel View Post
Alternatively, the control could do its own data management, the plugin could keep a pointer to the control (or you could look it up by index each pass), and the audio handler could just send the info straight to the control.
This is how I had it working initially, and am back to it for the moment.
In the processDoubleReplacing() function I'd send all the new wavedata to the custom control as it arrived using SetValueFromPlug, by calling SetValueFromPlug(*in1) within the 'for (int s = 0; s < nFrames; ++s, ++in1, ++in2, ++out1, ++out2)' loop. SetValueFromPlug writes each sample straight to a buffer owned by the control. Then, whenever draw() was called, it cycles through the buffer finding the minimum and maximum values for each vertical line that needs to be drawn. I'm thinking that as I want to be able to zoom in to the sample level it needs to have access to all the wave data.
Does that sound right? It seems robust, but feel I should explore the pointer approach above as well. Not least because it's such a fundamental part of c++ that I really ought to get a handle on how it works.


I used to be a reasonable whizz with programming, even got quite a way through writing an Alpha Juno emulation in Delphi back in the 90s. Left it all aside years ago to concentrate on writing and producing music, and coming back to it now is really confusing trying to bring all the stuff I used to know in line with all the new developments and technologies... also didn't really explore object oriented stuff that well so pointers . It's a bit like when you move onto a new DAW after years of in-depth use of another one - you feel like you should be able to do things so quickly but you can't!


Quote:
Originally Posted by ynohtna View Post
Are you familiar with the concept of ring buffers, Bobflip?

They're the common solution for a producer thread (your audio processing) to write data that is then read in a consumer thread (your UI), and can be implemented such that they don't require the use of threading synchronisation primitives.
Actually, yeah! Am using one here, but first learned about them for the Alpha Juno synth mentioned above. Hadn't heard the terms 'producer thread' and 'consumer thread' before though, good way of thinking about it!
Bobflip is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 01:48 AM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.