Draw an Envelope

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.

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

That would be great, thanks a lot!

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.  :(

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 :)

 

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.

- 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()

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.

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!  

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?

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. 

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

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:

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.

1 Like

(I don’t think it’s a very good example now, but I did put some envelope code up here. I don’t know if it’s any use.

https://github.com/jcredland/juce-toys/blob/master/adsr_screenshot.png

https://github.com/jcredland/juce-toys/tree/master/other)

2 Likes

Similar to bazrush - this is not a good guide to style but it’s a working example. It 's my old UGen++ library:

Have a look at the Examples/ModulesIntrojucerUGen project.

Replace the MainComponent code for this:

class MainComponent :    public Component
{
private:
    EnvelopeContainerComponent envelope;
    
public:
    MainComponent()
    {
        addAndMakeVisible(envelope);
    }
    
    void resized()
    {
        envelope.setBounds(getLocalBounds().reduced(4));
    }
};

The EnvelopeContainerComponent is the place to start looking. It uses the UGen++ library’s Env class as the model (which is kind of equivalent to the SuperCollider Env class). So it’s a bit difficult to use with other things without modification.

So you could do this to start with a given ADSR envelope:

    MainComponent()
    {
        addAndMakeVisible(envelope);
        envelope.setEnv(Env::adsr(0.05, 0.1, 0.7, 0.5));
    }

Here’s an example animated GIF

It shows:

  • click to add points
  • shift-click to remove
  • ctrl-click a line to edit the curve type
1 Like

Very fair points!! But more often it’s about looking at how other people have approached it which hopefully can some save time in creating your own solution in the end!

Thanks for your guidelines and code examples, I’ll definitely have a look at them as well!

Hey @lessp, what did you settle on here?

I’m doing something almost exactly the same; I have a working implementation with a DragAndDropContainer component and a couple “control point” components as children. Those points get dragged around and then I paint a path through the components by calling getLocalBounds and computing their center point. As I’m doing this I also write the curve that the breakpoint line takes into a fixed size buffer that my audio processor can reference as a lookup table.

That’s all well and good, but now I’m struggling with how to serialize my plugin state for getStateInformation and setStateInformation. I’d love to use the AudioProcessValueTreeState (https://www.juce.com/doc/tutorial_audio_processor_value_tree_state) for the other parameters in my plugin, but then how to add the envelope point values on top of that is a little confusing.

I could also try to figure out how to represent my control points as audio processor parameters and just fit them into the tree, but then a user could, for example, automate points on the envelope and that’s not exactly behavior that I want.

This thread has been really helpful, but if you have tips for this last 20%, I’d love to hear it. Thanks!

If you’re using the AudioProcessorValueTreeState class then you can add child ValueTree objects to the main ValueTree object inside the AudioProcessorValueTreeState object. Then this can be serialised/deserialised to/from XML as shown in the tutorial. But as these nodes won’t be associated with any parameters they won’t be automable.

Have a look at how the AudioProcessorValueTreeState::getOrCreateChildValueTree() method adds children for the parameters. This is a private method but you can write something similar to add your own nodes.

3 Likes

That was perfect! I did just as you said and it couldn’t have worked better, thank you!