MaxMsp/Gen Param -> Slider linking


#1

Hello everyone,
this is my first post, I’m a complete newbie and I need just a little help to push me a step further.

I figured my way out to build a VST based on my gen-exported code and attached a bunch of sliders to parameters using “processor.setParameter” one the editor side; everything is working fine but I can’t get to move sliders when I draw automation on parameters.
I tried using the AudioProcessorValueTreeState class but I don’t understand where to pick values from (since on the Cycling pre-coded project the DSP side doesn’t sit on PluginProcessor.cpp but on C74_GENPLUGIN and parameters too).

I know I’m being annoying posting this, tried everything I could (with ugly structures too) but that class is unreadable for me. Hope to find someone helping me just a bit, I’d love to know how to recall values from parameters off that C74_GENPLUGIN to attach them to sliders.


#2

Little update:

tried using @daniel TapeDelay code posted here to get things working.

Seems pretty logic to me and I think it should work correctly; my Processor didn’t have a “parameterChanged” so I declared and added one to make it equal to the example code.
Now, it compiles but Daw crashes when opening the plugin.

Before this I tried doing what The Audio Programmer did on this tutorial, still no luck:

Here’s the project if someone could help me (71kb)
https://mega.nz/#!21ogzabD!WSGMOeK4nF27Pw8ii28KQI7EZxxOXYP9tt2VN2Oob3Y


#3

Hi @Aptrn,
I haven’t looked at your code, but you said it crashes on startup.
Could you maybe post the method where it is crashing (enclosed with three backticks ``` on a single line to activate code formatting) as well as the output of the debugger, i.e. which line it crashed, what type like ABRT, bad access, assert (technically no crash), etc…
That would make it easier to help…
Cheers,
Daniel


#4

Hey @daniel,
I’m not really confident that this is what you asked me but if I comment out this line the VST won’t crash on opening.

mLengthAttachment = new AudioProcessorValueTreeState::SliderAttachment(processor.getValueTreeState(), C74GenAudioProcessor::paramLength, *mLengthSlider);

The main problem I’m facing is that in the Cycling project you already got Parameters set up (relative to the gen-exported code) therefore some sort of ValueTree is already going, the one I added seems redundant (to my noobie eyes).
Furthermore there are a copule things that do seem strange to me like the absence of parameterChanged() (which I added, see below) and some stuff in getStateInformation()/getParameter()/setParameter(), which makes me think there are things I’m interferring with.

I’m pasting below all lines I added to files to get the AudioProcessorValueTreeState semi-working (based on your TapeDelay code):

In Processor.h

void parameterChanged(const String &parameterID, float newValue) override;
AudioProcessorValueTreeState& getValueTreeState(); 
static String paramLength;

In Processor.cpp

	mState = new AudioProcessorValueTreeState(*this, nullptr);

	NormalisableRange<float> sliderRange(0.0f, 1.0f, 0.01f);
	mState->createAndAddParameter(LENGTH_ID, LENGTH_NAME, LENGTH_NAME, sliderRange, 1.00f, nullptr, nullptr);
	
	mState->addParameterListener(paramLength, this);

[...]

        void C74GenAudioProcessor::parameterChanged(const String &parameterID, float newValue)
        {
	     if (parameterID == paramLength) {
		mLength = newValue;
	     }
        }

In Editor.h
ScopedPointer<Slider> mLengthSlider;
ScopedPointer<AudioProcessorValueTreeState::SliderAttachment> mLengthAttachment;

And in Editor.cpp

    `mLengthAttachment = new AudioProcessorValueTreeState::SliderAttachment(processor.getValueTreeState(), C74GenAudioProcessor::paramLength, *mLengthSlider);`

As I said there are these lines already written in Processor.cpp:

void C74GenAudioProcessor::getStateInformation (MemoryBlock& destData)
{
// You should use this method to store your parameters in the memory block.
// You could do that either as raw data, or use the XML or ValueTree classes
// as intermediaries to make it easy to save and load complex data.

	char *state;
	size_t statesize = C74_GENPLUGIN::getstatesize(m_C74PluginState);
	state = (char *)malloc(sizeof(char) * statesize);
	
	C74_GENPLUGIN::getstate(m_C74PluginState, state);
	destData.replaceWith(state, sizeof(char) * statesize);

	if (state) free(state);

}

I’m sure this is not the right way to format code, please correct me if I did something wrong.
Thank you for your time, I’m trying to figure something out but I literally can’t :confused:


#5

Unfortunately I have zero experience with MaxMSP, so I can’t throw out guesses.
I downloaded your project, but UnrarX says it is no rar archive… Can you try uploading again with zip please?


#6

I got the pre-prepared project from here and edited the code in the Gen patch (into MaxMsp) and exported the relative code (for the DSP side of things). Then I could build directly my VST with no GUI but with Parameters (working and automation-enabled).
Then I opened Projucer and started adding my sliders and basic GUI elements and everything works fine but:
If I move sliders, I can control Parameters; If I automate parameters my sliders won’t move.
Then I researched and ended up here asking for help :confused:

Here’s the project as I got it now
https://mega.nz/#!i8R1RaIS!4eYZ4J1ge2SpivRhMc659bBZ2WJ7bdLs3vUW4R7wacM


#7

BTW this makes me wanna cry… :wink: malloc deserves to be banned, this is error prone. There is a new paradigm called RAII, you can have a read on google. I wanted to link the JUCE coding style guide, that covers that topic, but the web-devs keep hiding that document… :wink:

It can be done even without that, since the MemoryBlock already is the target memory to use.
I think it should look like that (untested):

void C74GenAudioProcessor::getStateInformation (MemoryBlock& destData)
{
    destData.setSize (C74_GENPLUGIN::getstatesize(m_C74PluginState), false);
    C74_GENPLUGIN::getstate (m_C74PluginState, destData.getData());
}

PS I read that it was not your code, so you are excused :wink:


#8

Sorry, same… “this is no rar file”… could be my fault…


#9

I couldn’t ever write something like that, I got no clue at all of what that means

trying one more time, this time 7zipped, pretty strange

Here’s the third
https://mega.nz/#!S1pwHLbZ!Z9fExdX1p2HqsdhngWUi1Mvp9Acdova6FXhCQ22aS68


#10

I am sorry, I am a Linux/Mac guy, you can throw at me zip, tar, gz, bz2… for everything else I have to go find a suitable tool…
Edit: moment downloading, just see it is a zip now


#11

I am not really an expert on Cycling MaxMSP, so I better leave that to the Pro’s, but at a glance it looks to me, that it is not meant to have two different states working at the same time…

Maybe somebody with MaxMSP experience can chime in here


#12

I don’t think it’s related to Max, more likely to how the State is handled in the pre-made code.
So what do I do now? Who can I ask about this?
And in second place, is what I did theoretically right, in a standard vst-slider-attachment situation?
I’d love to program the DSP in gen because is an environment that I already know (and I like a lot) but if there’s no way to make it working I’ll get started figuring how to code everything line by line.

Thank you for your time @daniel

Alessandro


#13

by the way, the code you posted says
"argument of type "void *" is incompatible with parameter of type "char *"


#14

Yeah, the coding standards page doesn’t help much if it can’t be found. Why not link to it from https://juce.com/learn?


#15

Ok, I didn’t know the signature of the method, but in that case it can be casted like:

void C74GenAudioProcessor::getStateInformation (MemoryBlock& destData)
{
    destData.setSize (C74_GENPLUGIN::getstatesize(m_C74PluginState), false);
    C74_GENPLUGIN::getstate (m_C74PluginState, (char*)destData.getData());
}

(I do a C-style cast here, since I saw that often used in the JUCE code base. It can be argued to use the more compiler friendly static_cast<char*> (destData.getData()) )


#16

can somebody point me out how to handle something like SliderAttachment off a CommonState class? Or some other method to call getParameter() frequently to pass it (if changed) to the slider component? (silly workarounds I know but got no other ideas)


#17

Had the chance to look a bit into… the generated Max code uses the old getParameter methods, that are no longer advised (though they should be around for legacy projects for a while).
You can add manual code in the stubs, but it is very kludgy:

int C74GenAudioProcessor::getNumParameters()
{
	return C74_GENPLUGIN::num_params() + 1;
}

float C74GenAudioProcessor::getParameter (int index)
{
	if (index >= C74_GENPLUGIN::num_params()) {
		// that's my boy...
		return;
	}
	t_param value;
	t_param min = C74_GENPLUGIN::getparametermin(m_C74PluginState, index);
	t_param range = fabs(C74_GENPLUGIN::getparametermax(m_C74PluginState, index) - min);
	
	C74_GENPLUGIN::getparameter(m_C74PluginState, index, &value);
	
	value = (value - min) / range;
	
	return value;
}

void C74GenAudioProcessor::setParameter (int index, float newValue)
{
	if (index >= C74_GENPLUGIN::num_params()) {
		// that's my new value normalised 0..1 - use it
		return;
	}
	t_param min = C74_GENPLUGIN::getparametermin(m_C74PluginState, index);
	t_param range = fabs(C74_GENPLUGIN::getparametermax(m_C74PluginState, index) - min);
	t_param value = newValue * range + min;
	
	C74_GENPLUGIN::setparameter(m_C74PluginState, index, value, NULL);
}

const String C74GenAudioProcessor::getParameterName (int index)
{
	if (index >= C74_GENPLUGIN::num_params()) {
		return "MyParameterName";
	}
	return String(C74_GENPLUGIN::getparametername(m_C74PluginState, index));
}

const String C74GenAudioProcessor::getParameterText (int index)
{
	String text = String(getParameter(index));
	text += String(" ");
	text += String(C74_GENPLUGIN::getparameterunits(m_C74PluginState, index));

	return text;
}

IMHO it would be better to rewrite the algorithm from scratch into a new project…


#18

wait,
if I call processor.getParameter(0) [which I know is the “length parameter” from C74_GENPLUGIN.cpp] I’m actually getting the value I want, I just need to pass it to the slider (something like a scoped pointer, this is where I need help).
I know they’re not advised but we can make an exception just for this right?:upside_down_face:
the code you posted is to add more parameters, or didn’t i get anything?

Maybe I’ll mail someone at Cycling asking to update the code in time for Max8 release…


#19

Lol… it’s you who will debug it… :wink:

Yes, that’s what I thought was your goal…

The methods are actually called by the host, so getParameter() should answer with the current value, setParameter should set the value in your algorithm (and your slider…)
In this case I would create a Value in the processor, in setParameter set the value, and in getParameter you get the parameter from cycling anyway… but you have to create the Value::Listener to update the value in cycling…

Value& Processor::getLengthValueObject()
{
    return lengthValue;
}

void C74GenAudioProcessor::valueChanged (Value& v) override
{
    // set value to cycling state
    updateHostDisplay();  // to let the host know something changed
}

void C74GenAudioProcessor::setParameter (int index, float newValue)
{
	if (index == 0) {
		lengthValue = newValue;
	}
	t_param min = C74_GENPLUGIN::getparametermin(m_C74PluginState, index);
	t_param range = fabs(C74_GENPLUGIN::getparametermax(m_C74PluginState, index) - min);
	t_param value = newValue * range + min;
	
	C74_GENPLUGIN::setparameter(m_C74PluginState, index, value, NULL);
}

// in editor constructor:
auto& obj = lengthSlider.getValueObject();
obj.referTo (processor.getLengthValueObject();

Good luck, you will need it!


#20

I’m not needing more parameters, I just have to listen to their changes when automated!
I’m already passing values from sliders to parameters using setParameter() (using a slider listener, that’s maybe the only thing I got working);
but don’t get how to peridiocally call getParameter() to pass the value back to the slider.setValue() to get the slider moving with the automation