Destiny+ Model Q2

Hi, New user, with lots of questions.
I’m working on a template for the Destiny+ Model Q2 (model-Q-2)
This synth only speaks NRPNs. Of which, before last week, I knew nothing about. Now I know a little bit, but I may still have some bits to wrap my head around.

I made a template, which sets a bunch of controls with message type ‘nrpn’, and set the relevant LSBs (afaict, MSB is always zero). When I send it to the E1, a few behaviours have me scratching my head

  • As far as I can tell, the template doesn’t collect the current state from the Q2, so my controls are all set at the default in my template, even though the synth may have that control at a different value. Do I need some lua magic to do a ‘query everything’ on startup ? Similarly, there’s a randomize button on the synth that after pressing, ideally would update the UI on the E1.

  • Some controls are 14 bit, quoting from the docs:

    • 107: Set BPM (0–65535 mapped to 1–300 BPM,update tempo/display)
    • 108: Set Algorithm Index (0–65535 mapped to 0–9, update display)
    • 109: Set Swing (0–65535 mapped to 0.0–1.0, update display)"
      I imagine I need to do this scaling with lua too ? As I have it set right now, with a lot of turning of the knob, I can see if update the bpm on the lcd from 1 bpm through 3bpm, so I know the nprn sending part is working at least.
  • There’s a bunch of other nrpns that I’m also suspecting are 14 bit values as lots of the knob movement results in either silence or very little difference. (The nprn documentation is very sparse, and isn’t clear on this). So again, more lua?

I’ve picked through a bunch of different E1 templates, but haven’t found a good ‘nrpn heavy’ synth that I could cargo cult from. Pointers welcomed!

thanks!

1 Like

Made some progress. My suspicions were right, that everything is a 14 bit nrpn.
The documentation mentioning 0-65535 is weird, given the max that can be sent is 16383. Using that as a max on everything does allow a bpm sweep from 10-300.
I think I need a custom formatter function to translate that in the E1 UI, but I’m happy I’ve been able to get this far without writing any code at all.

1 Like

solid progress tonight. It now makes differences to sounds when I adjust knobs which is really satisfying.

wip template at Electra One App

What’s missing:

  • The “pull current values from synth” as mentioned in first post
  • Likewise, hitting randomize on the synth doesn’t update the E1
  • Need to fill out the lua for send_nrpn, then the buttons on the 3rd page should work.
    (Why they decided to use separate nrpns for enable/disable instead of just 1, with a boolean value I don’t understand at all). If there’s code I can steal from another template to do that, I’d rather not reinvent the wheel.

Feedback welcomed, as mentioned above, before this week I hadn’t touched nrpn’s, lua or the E1 editor at all, so I’m stumbling my way through this.

2 Likes

@martin Some things I can’t get my head around that’s hopefully a simple question or two for you:

There’s a function in my code ‘toggle_fx’ which changes the meaning of one of the other faders. When I hit the toggle button, it does update the text/color of the fader but the underlying value remains unchanged when I try to write a new value using setValue()

Some debugging shows that this code..

local fadercontrol = controls.get(19)

value = fadercontrol:getValue("POLYNOMIAL")

is always returning nil, even though that’s what fader 19 is defined in the json as..

"id": 19,
"type": "fader",
"mode": "",
"visible": true,
"name": "POLYNOMIAL",

‘fadercontrol’ is getting the right object, because I call setColor on it, and it works as expected.

I do call setName on the fadercontrol object. I’m not sure if that would change the json at runtime? Even if so, it still gets a nil back from getValue when I pass in the variable I set the name to.

Unrelated confusion: The ‘regen filter’ in this template has a range of 0-16383. Even when I set the default in the editor to be 16383, it always starts up at zero. (This one isn’t backed by lua, so this might just be me driving the editor wrong)

thanks in advance for any clues!

There are two issues with:

First, the fadercontrol:getValue() returns a ValueObject, not the numeric value. In order to get the numeric value, you need to call a getter function on the ValueObject, like this:

local control = controls.get(1)
local valueObject = control:getValue()
local value = valueObject:getValue()
print("value = " .. value)

second, if a parameter is provided to the control:getValue(), it expects a valid valueId, ie. a text name of the value to be fetched. it is “value” for a fader, for adsr it can be any of “attack”, “sustain”, … When not provided it defaults to the first valueObject in the control. There is a list of valid valueIds in the docs.

The default value of the NRPN does not seem to work ok. I added it on the todo list.

1 Like

Thanks for the help.

I’m still missing something. I’m trying to set, not get the value. I tried this…

local fadercontrol = controls.get(19)

local valueObject = fadercontrol:getValue(“POLYNOMIAL”)

valueObject:setValue(1)

But that still seems to get a nil value from the the getValue call.

Same thing for getValue(fadername) (the same param as the setName call)

Also for just fadercontrol:getValue()

(also, feature request maybe: any way to make the stdout console at the bottom of the lua page auto-scroll when there’s new output?)

can you test this preset: Electra One App

It should print the default value of the control when loaded. It uses the approach I outline above.

auto-scroll, it actually is there but it stopped working in recent updates of chrome. I need to review that.

That does set the startup value, yes. But I want this to change later, from lua rather than the editor.

With your code, I can read the current value of what the fader is set to, but that’s not what I’m trying to do.

Let me rephrase, because I think I’m not explaining clearly.

I have two buttons. A fader, and a toggle.

If I turn the fader to for eg 50%, and then hit the toggle, I want the fader to reset back to 1%

(Basically the toggle changes the meaning of what the fader is set to, so I want to set it back to a ‘safe’ default on switch)

Trying this, with just an extra line at the end on your code ..

local control = controls.get(19)

local valueObject = control:getValue()

local value = valueObject:getValue()

print("value = " .. value)

valueObject:setValue(1)

results in this:

lua: value = 16383 ← correct, I had turned the fader to max.

error running function ‘runFunction’: ctrlv2/slots/b00/p00/main.lua:187: attempt to call a nil value (method ‘setValue’) ← Same object can’t be written to ?

o ok, sorry. I simply did not notice that you talk about setValue(). That function does not exist and so my brain switched to getValue(). Hence the nil value error - that error message puzzled me :slight_smile:

While setting values in Lua we force users (developers) to use the ParameterMap or the message:setValue() function. The reason is to think about changes in the same way as if they came via MIDI. Receiving a MIDI message or calling parameterMap.set() can trigger whole chains of function calls and modify many controls (according to how your preset is set up), doing that from a valueObject could be misleading.

Note, message:setValue() is just a shortcut to parameterMap.set().

Let me know if you need an example. I have a busy schedule today but I think I could do that in the evening.

1 Like

ah! This makes a lot of sense.

I read a little about parametermap yesterday. Still internalizing all the object model. I’ll play with this for the next few hours. If I don’t report back success, consider it a distress call for examples :stuck_out_tongue:

thanks again for your help.

jackpot! It took just a one-liner. (well, plus 2 variable declarations)

parameterMap.set(globalDeviceID, PT_NRPN, 19, resetval)

This sets me up to work on the next step, reading nrpns, and setting current state, but that’s tomorrow’s problem.

One small victory at a time, this is coming together.

2 Likes

This is a head scratcher. In my current template, I have a function “update_control” that looks like this..

function update_control(control_id)
    parameterMap.set(globalDeviceID, PT_NRPN, control_id, current_state[control_id])
    local control = controls.get(control_id)
    control:repaint()
end

It works for all the faders in my template, other than two (FADER_BPM, and FADER_SWING) controls 47 and 45 respectively.

I added this debug code at the end of the function to read back what the set should have written..

local valueObject = control:getValue()
local value = valueObject:getValue()
if value ~= current_state[control_id] then
print("control " .. control_id .. " didn't update. current_state = " .. current_state[control_id] .. " value = " .. value)
end

And sure enough, only for those faders, I get this printed out..

lua: control 47 didn't update. current_state = 10000 value = 0

Even more confusing, I changed the debug to do this..

    local value = parameterMap.get(globalDeviceID, PT_NRPN, control_id)
if value ~= current_state[control_id] then
        print("control " .. control:getName() .. "[" .. control_id .. "]".. " didn't update. current_state = " .. current_state[control_id] .. " value = " .. value)

This prints out:

lua: control BPM[47] didn't update. current_state = 2015 value = 16537.0

This is a NRPN control, how is it larger than 16383 ?

Again, this works for all the other faders. I’m at a loss to explain what’s special about those two faders.

(to demo it, hit the ‘rand all’ button in current template, and note the bpm/swing faders don’t update)

@martin is there something special about that 16537.0 number ? I couldn’t find any reference in the docs, but I’m wondering if this means something internal that’s undocumented.

I was beginning to think it meant something like unitialized, so added explicit code to init the parameterMap.set as the very first thing in my lua. Before reading it back but after the set, I read back that magic 16537.0 number.

parameterMap.set(globalDeviceID, PT_NRPN, 47, 8192)
val = parameterMap.get(globalDeviceID, PT_NRPN, 47)
print("BPM val = "..val)

00:38:01.630 lua: BPM val = 16537.0

I setup a new project with just one fader, and did identical code, and that worked as expected.

I can delete all my other lua other than those lines, and the bug persists, making me think it might be something about how the fader is configured in the UI. But they look normal afaics, and I don’t see anything unusual in the json.

Yes, it is a special value. the parameter map values are limited to the 14-bit range. There are a few magic numbers outside that range. I did not include them in the documentation as I thought that users would never come across them. The 16537 is a constant used to prevent to send the MIDI value out. It is used in situations such as a pad that should not send a MIDI message of certain state change, etc.

What you describe is a bit strange. I will take a look at it, is it still in this preset Electra One App ?

yes. same preset

bpm & swing faders are on page 3.

thanks for looking!

1 Like

I reviewed the preset. I hope I have not missed anything but I cannot see parameterNumber 47 (NRPN) defined anywhere in the JSON or Lua. The set() will not set anything and the subsequent call to get() will return 16537 (uninitialized).

When I change that part of the code to:

parameterMap.set(globalDeviceID, PT_NRPN, 107, 8192)
val = parameterMap.get(globalDeviceID, PT_NRPN, 107)
print("BPM val = "..val)

The value of 8192 is returned by the get() function.

I looked at it in more detail. If I comment the REGEN_FILTER = 24 out in the following code:

local FADER = {
    POLYNOMIAL = 19,
    REGEN_MIX = 18, REGEN_TIME = 23, REGEN_FEEDBACK = 20, REGEN_FILTER = 24,
    ALGO_INDEX = 46, ALGO_MIX = 17, ALGO_FEEDBACK = 25,
    ALGO_MOD_SPEED = 22, ALGO_MOD_RANGE = 21,
    BPM = 47, SWING = 45
}

the default value is set just fine. The preset is quite complex so it would take me too much time to find the reason, but maybe it gives you a pointer to where to look at.

With help of your snippets I think I’ve figured it out. I’m confusing NRPN numbers with fader numbers.

See above where I mistakingly did..

val = parameterMap.get(globalDeviceID, PT_NRPN, 47)

(47 is FADER_BPM)

Whereas your code (correctly) is doing

val = parameterMap.get(globalDeviceID, PT_NRPN, 107)

I’ve probably made the same mistake in other parts of the code too and will review when I get time.

thanks Martin!

1 Like

with some help from claude.ai with the refactoring, I implemented some fader ← → nrpn mappings, and it works great now! Thanks again Martin.

something wasteful I’m doing in this template that could be optimized is that I’m repaint’ing every control, even the ones on other pages that aren’t visible, which is a bunch of unnecessary work when interpolating between states.

Is there an easy way to map from a control object to the page it lives on ?