Weird Constructor / Posts / HexoSynth 2022 - Devlog #9 - MIDI Note and CC Handling

HexoSynth 2022 - Devlog #9 - MIDI Note and CC Handling

The HexoSynth modular synthesizer (programmed in Rust) got MIDI note and CC handling this week.

If you don't want to dig through the detailed log:

TLDR: Skip to the Devlog 9 Conclusion Section

Detailed Log

This is the day by day log of my progress. If this is too verbose for your interest, please skip down to the Devlog 9 Conclusion Section.

2022-08-10

First I published the Devlog #8. Afterwards I worked on the MIDI handling in HexoDSP and the nih-plug VST3/CLAP plugin. The rough idea has been sketched out, but there are still details to handle about sample accurate timing:

HexoSynth MidiP node is translating MIDI notes already somewhat... but by far not correctly yet.
HexoSynth MidiP node is translating MIDI notes already somewhat... but by far not correctly yet.

2022-08-11

Plugin/HexoDSP: The MidiP node makes slow progress. At least I hear a proper pitch now and also the gate seems to work almost. But reopening the DAW/reloading the plugin for each test is tiresome. I guess I will have to extend my test suite to also be able to feed note events into HexoDSP, just like the HexoSynth nih-plug backend does.

Later today I finished some foundation work on the test suite, finished the first MIDI test and even found the bug I was hunting. Here is the first test case for MidiP (Midi Pitch), which receives MIDI events and provides 'freq', 'gate' and 'vel' to the DSP graph:

#[test]
fn check_node_midip_gate_inserts() {
    let (node_conf, mut node_exec) = new_node_engine();
    let mut matrix = Matrix::new(node_conf, 3, 3);

    // Create a DSP matrix with a "MidiP" node and an Out node:
    let mut chain = MatrixCellChain::new(CellDir::B);
    chain
        .node_out("midip", "gate")
        .node_inp("out", "ch1")
        .place(&mut matrix, 0, 0).unwrap();
    matrix.sync().unwrap();

    // Test run for 5ms with 3 Note On events at sample positions
    // 5, 10 and 130 in this block of samples:
    let (ch1, _) = node_exec.test_run(0.005, false, vec![
        HxTimedEvent::note_on(5, 0, 69, 1.0),
        HxTimedEvent::note_on(10, 0, 68, 1.0),
        HxTimedEvent::note_on(130, 0, 57, 1.0),
    ]);

    // Collect the signal changes (raising edges):
    let changes = collect_signal_changes(&ch1[..], 0);

    assert_eq!(changes, vec![
        (5, 100),   // First note triggers right
        (11, 100),  // Second note needs to shortly pause the gate, which has 1 sample delay
        (131, 100), // Third note also shortly pauses one sample later.
    ]);
}

Having a test case is so important for finding bugs. You can insert debugging prints everywhere, without any audio crackling or DAW coming down screaming to you. And second, your test case will remain there, and make it possible to refactor things later on. No fear of fixing architectural bugs anymore.

2022-08-12

Plugin/HexoDSP: I further developed the test case for 'MidiP' today. I still have to think about how to exactly design the API to connect the HexoDSP engine to the nih-plug audio plugin wrapper. That will keep me busy for a few more days I guess.

2022-08-13

HexoSynth: I fixed the (hopefully) last bug in MidiP today and was able to finally play some MIDI sequences from Renoise and Ardour in HexoSynth. I also connected my old AKAI LPD8 pad controller and made some noises:

2022-08-14

HexoDSP: Implemented receiving MIDI events from the audio thread in the frontend thread.

HexoSynth: With the new MIDI events in the frontend, I implemented a MIDI log for checking which MIDI messages are received by the audio thread.

HexoSynth MIDI log window in action.

Next I implemented a MIDI learn functionality for the MidiP and MidiCC nodes. For quickly assigning controls without guessing MIDI channel or CC number.

2022-08-15

HexoSynth: Wrote two automated tests for the MIDI learn functionality. The scripted test suite in WLambda makes this a smooth process, which is nice:

HexoSynth test case written in WLambda for MIDI learning
HexoSynth test case written in WLambda for MIDI learning

2022-08-16

HexoDSP: Worked a bit on the gate length mode in the 'MidiP' node today.

I have to admit, that the heat wave is getting to me today. My workspace has heated up over the last days, and I can barely maintain a straight thought. I hate losing momentum like this.

YouTube Sound Demos

Tapping around with the MIDI pad controller and the new MIDI note node:

And me noodling around with the MIDI keyboard:

Devlog 9 Conclusion

This week I've been fully into the nih-plug plugin integration. There are still no plugin parameters you can automate directly from the DAW, but there are now MIDI Note and CC input nodes. Those allow you to get frequency/gate and velocity information, as well as MIDI CC to control any parameter you like.

I've also taken the extra step and implemented MIDI learning. Because getting the MIDI channel and CC numbers right is more often than not a bit of guesswork. I've also added a MIDI log window, so you can monitor if and which MIDI messages are being processed in the first place.

I'm not done yet, and the following point still remains on the roadmap:

  • Finish the nih-plug integration. That means a better integration of HexoSynth as VST3/CLAP plugin into your favorite DAW.

As well as the following points remain on the roadmap towards the first release of HexoSynth in 2022:

  • Add more automated test cases for the UI workflow.
  • Rewrite the online help.
  • Add inserting DSP chains that are to be pre-defined. And also inserting random DSP chains for a more explorative/experimental workflow.
  • Add back editing CV widgets.

Contact

In case you find this project interesting or have questions, you can reach me via Discord these days, check out the #hexosynth channel in the Rust Audio Discord. Optionally I'm also online on IRC (via Matrix) in the Linux Audio Developer channel #lad (nickname 'wct') on Libera.Chat: irc:#lad@irc.libera.chat.

Links

Weird Constructor Avatar
Weird Constructor

Nice end 30 y/o guy, geek and F(L)OSS developer that messes with: Linux, Windows, Networking, Interpreters/Compilers, Games, Audio, Music, GUIs, C/C++, Rust, Scheme/Lisp, 3D printing, electronics and more.


For updates follow me on Mastodon

All the F(L)OSS development on my projects happens in my spare time. If you find what I do useful, entertaining or just want to support me, you can do this via Liberapay:

Donate using Liberapay