Firmware to support BCR2000 Data Request

Hi all,

I’m currently hacking the Ambika firmware to provide a way to dump patch values from the Ambika to my BCR2000 (I assumed that it’s not possible as of today (?))

I have 6 templates (or ‘preset’) on the BCR, each corresponding to an Ambika part (I assigned a midi channel for each part on the Ambika).
The idea is to have the BCR updated when switching presets on the BCR (could be useful also after loading a patch on the Ambika).

This can be done using a so called ‘LEARN output’ message from the BCR, that sends a sysex (that Behringer calls a ‘Data Request’). I took a free message number (0x16) as the ‘Data request’ command, and wrote a handler in the Ambika firmware. When receiving the Data Request (with the part number as a parameter), the Ambika sends a bunch of CCs on the corresponding midi channel:

bc… case 0x16: {
const Parameter& parameter = parameter_manager.parameter(59);
uint8_t midi_channel = parameter_manager.GetValue(parameter, location.part, 0);
if (midi_channel)
–midi_channel; //?

for (uint8_t index=0; index<kNumParameters; ++index) {
const Parameter& parameter = parameter_manager.parameter(index);
if(parameter.levelPARAMETER_LEVEL_PATCH || parameter.levelPARAMETER_LEVEL_PART) {
uint8_t value = parameter_manager.GetValue(parameter, location.part, 0);
if ( (parameter.midi_cc < 1) || (parameter.midi_cc > 120)) continue;
midi_dispatcher.Send3(0xb0 | midi_channel, parameter.midi_cc, value & 0x7f);
midi_dispatcher.Flush();
}
}
}

And… it’ working! Well at least for many parameters except parameters that have the same ID but necessitate a stride to get the actual value (e.g., enveloppes). I cannot figure out how striding works. What I want to do is to get the midi_cc corresponding to parameters that necessitates striding.

Any tips?

I hope that all of this makes sense…

Cheers,

Stéphane

PS: I do not know how to format the code in this message, any pointer on a doc on how to do this? ok, I’ve found the sticky message in the forum about formatting

parameter.num_instances tells you how many variants of this parameter there are - so you should loop on this.

parameter.offset + parameter.stride * i will give you the memory address at which the value resides (the argument you have to GetValue - you are doing it wrong at the moment).

parameter.cc + parameter.stride * i will tell you the CC number.

1 Like

yes, I replaced parameter.cc-=… by parameter.cc+=…, much better now.

It’s kind of working now… except for one thing: after the Ambika has sent the CCs, it is in a weird state (values of both osc waves are ‘none’, some other values are negatives etc).

Of course, I had to plug the midi i/o of the Ambika to the midi o/i of the BCR in order to send requests and receive data:
BCR midi out -> Ambika midi in ;
Ambika midi out -> BCR midi in ;

When I inspect the values that Ambika sends using ‘Midi monitor’ on my Mac…:
BCR midi out -> Ambika midi in ;
Ambika midi out -> Midi monitor in ;

…the Ambika is not getting ‘corrupted’ (and the values seem clean).

I also added a command bound to S7 in the os_info_page menu that sends the CCs using the current part. With a single midi link:
Ambika midi out -> BCR midi in :
… pressing S7 updates the BCR, and the Ambika is not getting corrupted.

I suspect this has something to do with a midi loop, but I’m not sure.

(Of course, the targeted setup means that although I succeeded in controlling the Ambika with the BCR and vice-versa, I can’t send noteon noteoff with a keyboard since all midi ins are populated, the setup may require a ‘midi in merge’ somewhere… I’ll see that later)

EDIT: well actually, it’s not because of a midi loop, two of the presets work (corresponding to part 2 and 3), one does not (corresponding to part 1), still investigating.

It may come from the way I get the midi channel corresponding to a part: https://gist.github.com/conversy/cc6d5b329480a234744f

I am not quite sure if the instance_index I use should be the same than the location.part inferred from parsing the sysex…
There is something I’m missing obviously: I have to decrement midi_channel, though I suspect that the midi channel is stored as is…

midi loop after all…
Setting the BCR to ‘S4’ mode removes the problem…

Here are the files for those who want to use/test it!

Ambika-BCR.zip (58.9 KB)

back to business: I bought a midibox 4x4 for midi merge in and for real use with a keyboard. Unfortunately I discovered that my firmware is not working correctly: some of the values are not set up or “remembered” properly.
I suspect this has to do with my handling of parameters, stride etc. I tried to better understand what @pichenette said about the stride stuff and I came to this code:

bc… /* static */
void Storage::SysExSendAllCCs(uint8_t part)
{
// get midi channel of the part
const Parameter& parameter = parameter_manager.parameter(59);
if (parameter.offset != PRM_MULTI_MIDI_CHANNEL) return;
uint8_t midi_channel = parameter_manager.GetValue(parameter, part, 0);
if (midi_channel>0) --midi_channel; //??

for (uint8_t index=0; index<kNumParameters; ++index) {
const Parameter& parameter = parameter_manager.parameter(index);
if(parameter.levelPARAMETER_LEVEL_PATCH || parameter.levelPARAMETER_LEVEL_PART) {
// Some ranges of MIDI CC might point to the same parameter ID, for
// different instances of the same object (for example a LFO).
uint8_t midi_cc = parameter.midi_cc;
if ( (midi_cc < 1) || (midi_cc > 106)) continue;
for (uint8_t instance_index = 0; instance_index < parameter.num_instances; ++instance_index) {
uint8_t vorig = parameter_manager.GetValue(parameter, part, instance_index);
int16_t v = vorig;
uint8_t range = parameter.max_value - parameter.min_value + 1;
v = (v-parameter.min_value); // shift value to 0
v = v * 127 / range; // scale value to 0-127
uint8_t value = v & 0x7f;
if (midi_cc==14) {DebugPage::v1=vorig; DebugPage::v2=v; DebugPage::v3=value; DebugPage::v4=midi_cc; DebugPage::v5=parameter.min\\
_value; DebugPage::v6=parameter.max_value; }
midi_dispatcher.Send3(0xb0 | midi_channel, midi_cc, value);
midi_dispatcher.Flush();
midi_cc += parameter.stride;
//break;
}
}
}
}

There must be something obvious I must be missing but can’t find out what…

@stefanovic: Have you been able to solve this?

unfortunately not, I needed to focus on other projects.
Did you try the firmware?

you’ll wait one more year for reply

I did not try the firmware as I don’t have the Behringer controller, but I am interested in reading some of Ambika’s current program parameters for an external, hardware-based editor/controller. Here is what seems to work for the LFO trigger parameter:

  // LFO1 TRIGGER
  // for some unknown reason, passing "PRM_PATCH_LFO_SYNC" gets us cc# 46
  // so we pass the number 29 directly
  const Parameter& parameter4 = parameter_manager.parameter(29);
  midi_cc = parameter4.midi_cc + parameter4.stride * instance_index;
  value = parameter_manager.GetValue(parameter4, part, instance_index);
  midi_dispatcher.Send3(0xb0 | midi_channel, midi_cc, value & 0x7f);

  // LFO2 TRIGGER
  instance_index++;
  midi_cc = parameter4.midi_cc + parameter4.stride * instance_index;
  value = parameter_manager.GetValue(parameter4, part, instance_index);
  midi_dispatcher.Send3(0xb0 | midi_channel, midi_cc, value & 0x7f);

Now, if anyone happens to know why I can’t

const Parameter& parameter4 = parameter_manager.parameter(PRM_PATCH_LFO_SYNC);

(see comment in code above) but I can

const Parameter& parameter1 = parameter_manager.parameter(PRM_PATCH_MIX_OPERATOR);

please speak up!

one year later again…

My understanding is that the right way to do it is to pass ‘29’ to get the parameter, as you did.
AFAIU, PRM_PATCH_LFO_SYNC is an enum that is not related to the position in the memory.

Anyway, I decided to give the BCR2000 Data Request another try… And it seems I nailed it. I was not correctly requesting the midi_channel for the part. It should have been:

  uint8_t midi_channel = parameter_manager.GetValue(parameter, part, part);

and not

  uint8_t midi_channel = parameter_manager.GetValue(parameter, part, 0);

supposedly because this parameter is PARAMETER_LEVEL_MULTI.

Now it seems to be working: the values of the Ambika controls are uploaded at once on the BCR each time the preset is changed by the user on the BCR, and the LED rings reflect them.

The code, together with a YAM-based firmware and a set of BCR presets is available here: