Braids 1.5 MIDI Input

#1

(Disclaimer: This is not a request for assistance from Olivier!)

I’m having fun tinkering with Braids 1.5 code to add MIDI input via the RX pin.

> The goal is simply to take any incoming MIDI Note On (0X90) and use it to offset the current pitch as well as trigger the envelope (trigger_flag = true;). All other MIDI commands will be ignored for now.

1. The first thing I did was get rid of the unused UART Logger (braids/drivers/uart_logger.*) and replace it with the Yarns MIDI IO driver and rename the Yarns references inside these files:


2. The next step was to add Yarns’ MIDI IO library to braids.cc and strip out unneeded MIDI processing, eg. Sysex, realtime messages, etc.:

// #include “braids/drivers/midi_io.h”
// #include “stmlib/midi/midi.h”

// MidiIO midi_io;

Everything is still compiling so far.

3. Now comes the interesting part. At this point I just want to parse the incoming midi note number and offset the current reference pitch.

Both Yarns and Edges have a full MIDI buffer & stack that handles polyphony, etc. My assumption is that none of this is needed if I just want to focus on each newest incoming midi note:



4. The main question is where and how to convert midi notes to pitch - and where to inject them.

a) To start off, braids.cc reads the incoming Pitch CV (adc.channel(2)) and offsets it by the FM CV (adc.channel(3)).

b) There’s also a variable called midi_pitch in lookup_tables.py which generates an array:

// # Create table for pitch.
// a4_midi = 69
// a4_pitch = 440.0
// highest_octave = 128
// notes = numpy.arange(
// highest_octave * 128.0,
// (highest_octave + 12) * 128.0 + 16, 16)
// pitches = a4_pitch * 2 **** ((notes - a4_midi * 128) / (128 * 12))
// increments = excursion / sample_rate * pitches
// delays = sample_rate / pitches * 65536 * 4096

// midi_pitch = 128 * (69 + 12 * numpy.log2(frequency / 440.0))
// midi_pitch = max(midi_pitch, 0)
// modified_pitch.append(midi_pitch)

And finally, both the analog and digital oscillators used midi_pitch to set their pitch:

// uint32_t AnalogOscillator::ComputePhaseIncrement(int16_t midi_pitch) {
// if (midi_pitch >= kHighestNote) {
// midi_pitch = kHighestNote - 1;
// }
// int32_t ref_pitch = midi_pitch;
// ref_pitch -= kPitchTableStart;
// size_t num_shifts = 0;
// while (ref_pitch < 0) {
// ref_pitch += kOctave;
// ++num_shifts;
// }:


  • Idea 1: offset midi_pitch by the incoming midi note value

  • Idea 2: this line in braids.cc (or sometime right before when the min/max pitch values are checked) might be a candidate to offset the pitch, provided the midi note can be converted to an equivalent variable, since there’s an “lut_oscillator_increment” table in resources.cc:

// osc.set_pitch(pitch + settings.pitch_transposition());

I’ll post a link to my Github fork shortly.

(I’ve programmed lightly in C for years but no C++, so this is all still oblique to me)

#2

HELLYEAH!

#3

My Github fork: https://github.com/sneak-thief/eurorack/tree/master/braids

Re. MIDI input with Braids 1.7: There’s a small chance it would be possible if I strip out things like the Marquee, the Easter Egg (Paques) and maybe even the CV input display. I won’t know until 1.7 is uploaded.

#4

aside from the technical achievement of implementing this, what’s your use case for this? lose an additional midi>cv box? are there any other benefits?

#5

oootini: I built 5 Braids in a skiff with a 5-channel mixer/panner :wink:

#6

@ootini, you’d lose one midi->cv for each Braids you have, since each Braids would listen to its own midi channel.Although MIDI-through could also be implemented, it would be simpler to use a MIDI splitter such as this to send one MIDI source to multiple Braids.

#7

ps. here’s a super-easy midi splitter

If the midi note input works well, then midi Program Changes for modes, pitch bend and midi CC for color/timbre may also be within reasonable reach.

#8

It might be worth sending the decoded MIDI note/pitch to the LED display in the first instance, so that you can check that the MIDI handling works as expected, rather than having to use the serial port for both MIDI input and debug output, or having to use the JTAG interface. Actually, displaying the MIDI note values when in MIDI mode would be cool. No incoming MIDI note, it displays the oscillator mode as normal, but as soon as a MIDI note is received, it shows it’s value (as MIDI number, or in hertz, or as an alphanumeric note value). One second after MIDI note stream stops, the display reverts to the oscillator mode display. Should be doable.

Of course, a configuration option for setting which MIDI channel to respond to would be needed, in which case one of the options should be NONE or OFF, which just turns MIDI RX processing off. Another should be ALL or ANY, and 1 to 16, of course.

#9

Not sure how useful displaying Hz of an incoming note would be if the midi notes are only acting as a reference-pitch/offset for the entire module.

I’m inclined to set the default midi note value to the very middle (64) on boot, then any note above or below 64 will be used as a semitone offset.

In any case, I think I’ll start trying to display variables on the screen such as midi_pitch and pitch in order to see what’s happening internally before trying to inject anything.

As for setting the midi channel, I’ll hardcode it to 1 and worry about that later.

#10

Just a note: set_pitch( ) gets a 14-bit value, 7 highest bits = MIDI note, 7 lowest bits = fractional value.

All my projects are using this unit/representation for pitch.

#11

@Olivier: Merci! C’est précisément ce que j’avai besoin pour ce p’tit truc :slight_smile:

Have a great show-and-tell tomorrow.

@BennelongBicyclist: OK, we have the relevant data. Time to get to work heh

#12

#13

Amazing!

#14

@Sneak_Theif, note this comment on Olivier’s Facebook page:

  • Spent some time trying to shrink Braids’ code size to have at least some space for future bug fixes (there are now less than 1kb left!).
#15

Yes, the 1kb limit for 1.7 has been noted.

Updates:

1. In order to test my midi optocoupler interface, I compiled the code for Yarns with pin settings that were “compatible” with Braids. I selected Yarns’ Oscillator mode and was able to play notes over midi. Hearing Yarns sort of running on Braids was pretty amusing.

2. I’m pretty stupid when it comes to C++ (compared to C): As I mentioned previously, I used Yarns’ midi_io driver and imported Yarns’ midi_handler.

3. I tried to temporarily bypass the midi-channel checking code in midi_handler to avoid loading multi.c (the multi-channel midi-handling code), but this is likely where I’ve gone wrong:

– static void NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) {
– // if (multi.NoteOn(channel, note, velocity) && !multi.direct_thru()) {
– Send3(0x90 | channel, note, velocity);
– braids::midi_trigger_detected_flag = true;
– braids::midi_rx = note;
– }

Note: I added a couple of variables to try to pass the pitch to the oscillator and to trigger the envelope. I probably declared these incorrectly :stuck_out_tongue:

I can’t even figure out how to acknowledge that midi has been received. As I said, the differences in C and C++ are driving me nuts.

4. re. braids.cc: I also added a bunch of stuff but still can’t get any indications that midi is being received, let alone processed:

#include “braids/drivers/midi_io.h”
#include “braids/midi.h”
#include “stmlib/utils/ring_buffer.h”
#include “braids/midi_handler.h”


– void SysTick_Handler() {

– // Try to read some MIDI input if available.
– if (midi_io.readable()) {
– midi_handler.PushByte(midi_io.ImmediateRead());
– }

– void Init() {

– midi_io.Init();
– midi_handler.Init();

– int main(void) {
– Init();

– while (1) {
– while (audio_samples.writable() >= kAudioBlockSize) {
– RenderBlock();
– }
– ui.DoEvents();
– midi_handler.ProcessInput();
– }

…oh well, I’ll keep trying to figure this out.

#16

I was wondering if anyone found the solution to have Braids reacts to MIDI messages like pitch and note ON/OFF, or if Sneak_Thief got it to work?

1 Like