Draw an Envelope


#1

I would like to draw my own envelopes (ADSR) and being able to edit them with the mouse within the interface. I'm reading about the Graphics class but I'm not sure where to start. Are there any examples for something like that? Where should I start?

Thanks.


#2

One way would be to have an envelope component that draws lines, with child components on top for the handles.  You could use this: 

http://www.juce.com/api/classComponentDragger.html

To handle the dragging. 

And use: 

http://www.juce.com/api/classComponentListener.html

to detect when the handles had been moved. 

Then redraw the main component with the appropriate lines on it. 

There's probably a few ways of skinning this particular cat though...


#3

Thanks for the reply. I'm studying those classes, but it seems quite complex for a beginner like me.

Do you know if there are any examples I can read?


#4

Not of that off the top of my head.  

Start simple, have two components, one small on top of (and a child of)  a big one.  

Draw a rectangle in the small one. 

draw(Graphics & g) override { g.setColour(Colours::yellow); g.drawRect(getLocalBounds()); }

will draw a rectangle the size of the component. 

Next see if you can get it dragging around. 

After that move on to a point in the big one following the small one around. 

After that you'll have probably worked out all you need to get started with an envelope... 

 


#5

Check out drowaudio which has a 2 point example… But I’ve found this pretty difficult to implement for more points without having to modify the JUCE code.

Rail


#6

Or.....

http://www.juce.com/forum/topic/breakpoint-envelope/function-component-0

Thanks for Martin for posting this.


#7

Thanks for the replies. I tried to compile Martin's code but there are several errors, probably it was made for an older version of Juce?

I'll have a look at drowaudio and I'll try bazrush suggestion.


#8

If I get a chance later I will update the code for you. I don't think it will take much work. 


#9

That would be great, thanks a lot!


#10

Here you go. Just create a new project with the Introjucer and drop these files into the Source dir.

 

p.s. It's very frustrating that one can't upload zip files, or even .cpp/.h files to the forum.  :(


#11

Thanks a lot! It worked and it's great. Now I have to figure out how to adapt it to a standard ADSR in my plugin :)

 


#12

Ok, following the examples I managed to build a compoment with 4 child components (small squares) that I can drag around.
Now I have a few questions:

- How can I restrict where to drag those child components? (for example, not outside the parent component, or not over/behind another child compoment like attack can't go over decay etc..)

- How can I draw lines/curves between two points?

- How can I draw a circle instead of a square to use as child component?

 

Thanks in advance.


#13

- How can I restrict where to drag those child components? (for example, not outside the parent component, or not over/behind another child compoment like attack can't go over decay etc..)

It's been a while since I really looked at this code, but I think there are methods for getting the next, and previous handles. You can just test against their positions. 

- How can I draw lines/curves between two points?

Lines ARE drawn between the points? See  MyEnvelopeComponent::paint(Graphics& g), if you want curves just change the lineTo() call to a cubicTo, or quadTo, sorry I can't recall the name of those functions of the top of my head, check the docs. 
 

- How can I draw a circle instead of a square to use as child component?

Look at MyEnvelopeHandleComponent::paint()


#14

OK thanks a lot Rory, I managed to get a working ADSR graph. Looks really cool.

One last question/problem, how can I use ComponentListener? Should I include it in the component class like

class MyEnvelopeComponent : public Component, public ComponentListener ?

I'm not really sure how to do it.


#15

Yeah, that's right, but you will need to override the virtual ComponentListener methods you want, for example componentMovedOrResized(). Then you will need to use the Component::addComponentListener() method to register the listener with your component. I've never used ComponentListeners, because I was not aware of them until just now!  


#16

Sorry to bother again but I really don't understand how to use ComponentListeners.

So far I put public ComponentListener in the class, then added void componentMovedOrResized(Component &component, bool wasMoved, bool wasResized); in the public section of that class.

Then what?


#17

you then need to register the listener with your component using addComponentListener(). If you do this correctly then the componentMovedOrResized() method should be called whenever your component is moved or resized. 


#18

Hey Rory, do you happen to have this still, and if so, do you mind uploading this again?


#19

I’m afraid I don’t. I just prepared that sample for @lapsang. Over the years I’ve come to think that Jules purposely leaves out ready-made components such as this so people would take the time to develop them themselves, and learn a whole lot in the process. Or maybe I’m just giving him too much credit! :smirk:


#20

After having written several different envelope editors - since the one mentioned above - Jules is almost certainly right to leave this out of the library. There are many many ways of doing this and they need to be compatible with the audio engine side of things. It’s not impossible, but it’s really hard to make a general case that would satisfy everyone.

It’s not too hard to make have a few draggable components and then then draw paths behind them between the points. And as Rory says, it’s a very good learning exercise!

More broadly, my advice would be to write the model of the envelope first, then write the the UI. For example, is it just a plain ADSR or maybe an AHDSR, or a completely flexible envelope? Can the curves between points be changed or are they just linear transitions? If they are curves then what shapes? Are they variable concave/convex curves, or maybe a sine curve? Then you have the issue of representing sustaining envelopes. Does the envelope just stop at the sustain point? Or can it loop between points?

Once you have that completely decided you can more easily approach the UI and have some way of the UI modifying the model, and likewise the UI drawing what is represented by the model.