Ambika MPE support?

Hi all,

I do not think the Ambika supports MPE, or does it? That would be really awesome. Anyone working on this?

Regards
Björn

No it doesn’t.

But it is does support polyphonic aftertouch!
Cheers

(quick question, is there a docker image to build the firmware? I only remember having to install a bunch of stuff on my osx to flash the chips)

I’d interested in implementing per channel aftertouch on the ambika to use with my continuum. I tried to understand a bit the ambika code and first searched in the firmware code using (https://github.com/pichenettes/ambika/search?q=Aftertouch&unscoped_q=Aftertouch&type=Code). But I’m not sure i understand the flow of midi data. Seems like

  1. controller.cc is calling a function to pool if there’s new midi data and also supports running_status to minimize midi data.

int main(void) {
Init();
ui.FlushEvents();
while (1) {
// Do some MIDI.
while (midi_in_buffer.readable()) {
midi_parser.PushByte(midi_in_buffer.ImmediateRead());
}
// Do some LFOs and clocks.
multi.UpdateClocks();
// Do some display.
ui.DoEvents();
}
}

Ok, looking at void MidiStreamParser<Device>::PushByte(uint8_t byte) in midi.h we see that realtime stuff gets processed and skips the whole checking:

if (byte >= 0xf8) {
MessageReceived(byte);

It then checks on hi value to adjust parameter #s and sets polyAT and CCs expected_data_size_ to 2 and channel pressure to 1, which is expected. Ultimately, after checking we’re not dealing with sysex we set

} else {
running_status_ = byte;
}

Eventually, we keep on parsing the midi stream (filling data_) until we have the right amount of parameters:

if (data_size_ >= expected_data_size_) {
MessageReceived(running_status_);
data_size_ = 0;

Where we will call MessageReceived(running_status_) which contains the type of MIDI message as it was filled up with our command byte.

Early into this function we have

// If this is a channel-specific message, check first that the receiver is
// tune to this channel.
if (hi != 0xf0 && !Device::CheckChannel(lo)) {
Device::RawMidiData(status, data_, data_size_, 0);
return;
}

Now, as far as I can see, CheckChannel is:

static uint8_t CheckChannel (uint8_t channel) { return 1; }

which translates to if (true && false) in case of a non system exclusive msg, so we keep on going.

Eventually we arrive at switch (hi) for polyAt and channel. When we get to channel pressure we have:

case 0xd0:
Device::Aftertouch(lo, data_[0]);
break;

Which I’m guessing should be this one, with interesting use of velocity instead of pressure :slight_smile:

static void Aftertouch(uint8_t channel, uint8_t velocity) {
multi.Aftertouch(channel, velocity);
}

which leads to

static void Aftertouch(uint8_t channel, uint8_t velocity) {
for (uint8_t i = 0; i < kNumParts; ++i) {
if (data_.part_mapping_[i].receive_channel(channel)) {
parts_[i].Aftertouch(velocity);
}
}
}

So, after here we lose track of the channel reference as we will call parts_[i].Aftertouch with only the velocity (pressure?) which leads to sending it to everyone:

void Part::Aftertouch(uint8_t velocity) {
WriteToAllVoices(VOICECARD_DATA_MODULATION, MOD_SRC_AFTERTOUCH, velocity);
}

I’m wonder if Oliver can give some tips on the best way to add MPE-like channel pressure per channel. My idea was to change the multi.h to actually send to the channel the pressure level.

has anyone figured this out yet? is MPE support even possible?

I’m coming back to this project… but I still need to figure out the best way to compile the firmware on my Mac and I’m assuming there’s no debugging possible. I wonder if I can use the screen for debugging

1 Like

Ooohhh… this would be great. Although, if it isn’t working yet, it might be best to wait until the midi 2.0 spec is released. I suspect it will offer a tidier way to implement poly expression.

edit: never mind… it seems that 2.0 will not work over the 5-pin DIN.

Not sure if anybody else is still working on this, but I’ve made some progress. I’ve got full MPE working, but it’s not perfect, so I’m trying to resolve it. It doesn’t help that this is my first project ever in C++, but I figure I’m up for it…

I’ve changed the Multi and Part classes to pass channel information. This is required because the CC74 and Aftertouch data don’t carry note information, and must be associated to the correct voice using the channel. Unfortunately, I run into problems when I add the channel data to the VoiceAllocator (depending on how I do it).

It seems that the Library page develops some unpredictable bugs when I make some small changes to the VoiceAllocator initialization. When changing the size of some of the properties from uint8_t to uint16_t (or other similar changes that would presumably increase the memory usage of the voice_allocator), the library page will either not load, or it will jump through presets somewhat unpredictably. I’m assuming there’s some sort of conflicting memory usage but I’m not sure where to look on the Library page side of things to resolve it.

Any thoughts?

I’m afraid you can’t. The Ambika controller code was so close to the allowed RAM and flash usage that tiny changes like this could very well push it over the limit.

Thanks… that explains a lot of the design choices you made (and a lot of the issues I’ve been running into).

Presumably I could cut out some features that I don’t need in order to free up some space.

Thanks for making these things open-source btw… I’m learning a lot that I probably wouldn’t have otherwise!

1 Like