Pop pickup function

I’m trying to make a menu for the big genes where when I cycle trough the menu, the previous values of the pots are saved and if you want to change the value, you have to turn this pot beyond the saved value in either direction. So called “pot pickup” I believe, although I’m not sure of this terminology.

The function I wrote for this works in three parts.
Suppose we want to change the pitch of osc1 or osc2 using the same pot.

in inlets
P1 frac32.positive

in Local Data I define

char* state[2] = {"osc1", "osc2"};
int numstates = *(&state + 1) - state - 1;
int currentstate;
int prevstate;
bool ppwait1; // a variable that tells me to wait for the pot
int osc1pitch_hold; // a variable to store the value when in other menu
char osc1pitch_c[10]; // var to display with a bar the variable
int osc2pitch_hold; // a variable to store the value when in other menu
char osc2pitch_c[10]; // var to display with a bar the variable


// pot pickup function
int32_t ppchange (int in, int* hold, bool &ppwait){
//	if (timer==0) LogTextMessage((char*)(ppwait));
	if (ppwait==true){
		if (abs(in - *hold)>>20 < 2) {
			ppwait = false;
		}
		return *hold; ///goes to out!
	} else {
		*hold = in;
		return in; ///goes to out!
	}
}



//append int tot str for displaying a value
char* addItoS(int i, char* s, char* c){
	int32_t v = __SSAT(i,28)>>21;
	strcpy(&c[2],s);
	c[0] = 1;
	if(v >= 0)
  		c[1] = (uint8_t)(v + 1); // must not be zero !
	else 
	  	c[1] = (uint8_t)(256 + v); 
	return c;
}
[potpickuptester.axp|attachment](upload://yEAWD9ljXnC2rkLZB0KTYjijPKs.axp) (5.7 KB)



I also send the values out using this adapted midi function

//midiCCthin for sending the values to my controls in the patch
void midiCCout(int v, int cc, int channel){
	if (((lsend>v+(1<<19))||(v>lsend+(1<<19))) && (timer>60)) {
	   lsend = v;
	   PatchMidiInHandler(MIDI_DEVICE_INTERNAL ,0,MIDI_CONTROL_CHANGE + (channel-1),cc,__USAT(v>>20,7));
	   timer = 0;
	} else {
		timer++;
	}
}

I init my values in init code

//osc1
osc1pitch_hold = 29<<20;

//osc2
osc2pitch_hold = 29<<20;

and somewhere in K-rate code i do

if (state[currentstate] == "osc1"){
	outlet_S1mode = 0;
	midiCCout(ppchange (inlet_P1, &osc1pitch_hold, ppwait1), 1, 1);
	outlet_S1L1 = addItoS(osc1pitch_hold,"o1frq",osc1pitch_c);
}


if (state[currentstate] == "osc2"){
	outlet_S1mode = 0;
	midiCCout(ppchange (inlet_P1, &osc2pitch_hold, ppwait1), 2, 1);
	outlet_S1L1 = addItoS(osc2pitch_hold,"o2frq",osc2pitch_c);
}

This works,. but only sometimes so it seems. As soon as the patch grows, and the menu is getting bigger, it starts dropping values.

Is there a better way to do this?

I made a very minimal version, but I’m sure this used to work while now it fails at even the simplest combo. What am I doing wrong?

Wow looks complex. I haven’t had time to dive in but it looks like “prevstate” is never updated to equal “currentstate” after a change is registered.

Strings are mildly painful to work with, you could check some of the existing string/ objects like concat and append which seem to effortlessly handle string actions in just a few lines. It often comes down to if and where the \0 null terminator is encountered.

Really. Strings in embedded will be tricking you. Are they multidimensional arrays, or pointers? or multidimensional pointers? To “chars” or “ints”? Will ‘0’ mean 32, and will 0 mean ‘\0’?

At this point I wouldn’t even trust if this line will always work as expected:

if (state[currentstate] == "osc1"){

So it may be easier to not compare strings to each other, instead create an enum which will keep track of which parameter is being aimed at:

enum State {
  OSC1,
  OSC2,
  NUMSTATES
};

With the added bonus that NUMSTATES (here 2) will easily give you the length of your list.

Then (or I might be wrong) pointing the string array to the respective index using this enum will make things easier?

you’re right about the state not being saved, that was a mistake while copying. fixed now so the minimal patch (same updated link) now works.
also limited the encoder to the menuvalues, so no more crash when turning the encoder past 1.

So the minimal example now works as it should.
But from here on, I keep adding stuff to the menu, and at some non-deterministic point, it just starts failing. Then some menu’s work, and some don’t.

How is ksoloti comparing to the original axoloti sram wise? Could it be that the internal midi buffer is overflowed? although I’m only sending midi for the enable menu item, it is still doing this at k-rate, could that be a problem? (8 midi CCs per k-rate sample) + display updates

I also get regular crashes and audio stuttering when the midi-monitor is in the patch. Known Issue, I’m aware, but I actually never experienced this before

Not sure if this will make a difference, but it’s strings (buach), so it might be good practice to reduce the checking:
After all, state[currentstate] can only be one of the values at a time.

Once you’re more certain about the names, shortening them to “o1” or “o2” might also be a good idea, to avoid constant looping around comparing one char to another.

if (state[currentstate] == "osc1"){
	outlet_S1mode = 0;
	midiCCout(ppchange (inlet_P1, &osc1pitch_hold, ppwait1), 1, 1);
	outlet_S1L1 = addItoS(osc1pitch_hold,"o1frq",osc1pitch_c);
}


**else** if (state[currentstate] == "osc2"){
	outlet_S1mode = 0;
	midiCCout(ppchange (inlet_P1, &osc2pitch_hold, ppwait1), 2, 1);
	outlet_S1L1 = addItoS(osc2pitch_hold,"o2frq",osc2pitch_c);
}

**else** if (state[currentstate] == .......

Or even better, constrain all the string checking to when currentstate actually has changed. Sending MIDI at k-rate can’t be good - MIDI is 31500 baud, so one short message of 3 bytes will take at least (3*8)/31500 = 0.76 ms? could be wrong. Anyway, point is, even if the MIDI data is thrown into a buffer and sent out automatically, one k-cycle is only 0.333 ms so the next MIDI message will be haunting the earlier one before it is even half done, even with thinning? Bit hard to explain what I mean, really. I am thinking of once you turn the encoder while a MIDI message is still being sent.

If your string checks go inside the currentstate != prevstate check, you’ll need additional buffer variables for outlet_* though, as those must be written at k-rate or they will go haywire. so something like:

if (/*statecheck*/) {
    if (/*stringcheck*/) {
        _S1Mode = something;
        _S1L1 = something;
    }
}

/* each k-cycle */
outlet_S1mode = _S1Mode;
outlet_S1L1 = _S1L1;

Definitely get rid of the strings, in C this is not how you compare strings: "apple"=="apple"

That is comparing the address of each string and you cannot be guaranteed that the compiler is only storing one “apple”, it might have two of them at different addresses.

To compare strings you should compare the contents, using something like strncmp() or do it yourself with array indexes state[3] == '1' would compare the 4th character to “1”.

As @Ksoloti has said change it to use an enum, as soon as possible, it will make like easier :slight_smile:

Also if you need the string representation for some reason, to display or something :

char* stateStrings[2] = {"osc1", "osc2", "numstates"};

typedef enum _State {
  stOSC1,
  stOSC2,
  stNUMSTATES
} State;

char *getStateString(State state)
{
  return stateStrings[state];
}

Ok,

I’ll be converting my bigger with the above tips. It works on the small one. I’ll see how much time it takes to fully implement this in a bigger patch
Not yet figured out how to send midi at less then K-rate without running a timer at k-rate. but I changed the timer from 60>600 and I still have responsive midi controls with 10 times less messages…currently the timer is dependent for how many controls are “activated” so if more controls are enabled, timer++ is run multiple times and more messages are sent… a bit weird.

One thing you could do is to have a check saying: Has the midi CC value changed, if so send it otherwise don’t send it.

Ok, the switch to enum makes a h u g e difference. This was probably the main cause. I managed to get a bigger patch to work now so yeah looks good now.
Took me a while to convert my complexer patches, but seems like it’s worth it :slight_smile: THANKS!

1 Like