Write patches in Rust!

Hi,
I’ve been putting together some framework code to allow writing patches in Rust, and I think it’s just about ready for other people to try using it:
https://github.com/orukusaki/owl_patch
All the basic features are implemented, and the examples patches work on my Lich using Owl2 and Owl3 modules. There are still some more advanced features to add in future - see the README for details.
I’ve not included a DSP library - users are free to find or create their own. One of the examples uses the FunDSP crate, which works ok, despite not being the most highly optimised for the Arm MCUs.

Anyway I’m not sure how many people this will interest, but if you are interested in trying to write patches in Rust, please give it a go and let me know how you get on.

1 Like

Now that is an unexpected surprise. I tried doing exactly that a few years ago, got stuck on maybe 40% mark due to running too deep into certain Rust topics I didn’t understand well at that time. Anyway, congratulations on making something that actually works.

If you’re interested in feedback, I would suggest exposing fast exp/log functions in some way, they are defined here - https://github.com/RebelTechnology/OwlProgram/blob/develop/LibSource/basicmaths.h

They use LUTs stored in firmware and accessed with service calls which you expose, so it shouldn’t be too hard to use it from pure Rust code.

Also, there’s FFT/IFFT code that uses LUTs from firmware in a similar fashion. It looks like this is a clone of relevant functions from CMSIS that you might be able to use with those tables, so no need to reinvent the wheel here or link with C library.

Unfortunately OWL itself has been in hibernation for over a year and there’s no signs that we should expect any changes in this regards. If this ever changes, I’m sure that Rust support could be added to the library.

Thanks for your response and feedback :slight_smile: I have those LUT things on my todo list. I think the big thing will be working out how they can co-exist with / enhance existing maths crates: micromath, and libm, plus cmsis_dsp as you suggest.

I’m aiming to keep my code fairly bare-bones so I’d prefer not to port over all all the maths stuff from the C repo - but perhaps compiling and linking to it will be the way forward.

One more thing to keep in mind is that those tables are considered optional, so fallback code is needed if they’re not present. In practice, LOG/EXP tables are shipped with firmware for all devices, FFT is also everywhere except Xibeca board (used for AC/DC only at the moment).

Another thing that’s worth porting from C++ OWL library is V/Oct conversion. The math itself is fairly simple, but it relies on reading calibration settings using a service call

I’ve added Volts per Octave conversions! Will probably do log/exp/fft tables next, then loading resources.

I’ve released a new version which has the log/exp Fastmaths stuff implemented, plus loading Resource files. It’s a published crate now too: https://crates.io/crates/owl_patch
FFT service calls are still todo, hopefully I will be able to add that soon. Trying to think of the simplest example patch that demonstrates the FFT calls working, can someone suggest something I could tackle that’s simpler than a convolution reverb?

Glad to hear that you’re still working on this. The simplest FFT would be a resynthesizer where run code through FFT and then IFFT which produces audio that is identical to input (plus some artifacts that you get from transforms).

A less boring patch would be shifting magnitudes left/right with a parameter controlling shift amount and direction. I have an example of that in C++ at MyPatches/C++/FFTSpectralShiftPatch.hpp at master · antisvin/MyPatches · GitHub , hopefully it still works.

That examples uses a class I’ve written that deals with windows and buffering, because you want to run FFT on large buffer (something like 512 or 1024) and audio buffer is usually much smaller. You’ll also have to compensate overlapping windows, because otherwise you get a sort of ripple in amplitude for some window functions. To avoid this complexity, a good real world example would be reading a wavetable, running FFT on it, then processing all bins with several parameters for doing spectral effects). The results can be used for a WT oscillator. As a starting point for spectral effects you can peek at WaveEdit sources

Convolution, besides reverbs, can be used as a filter with arbitrary response (by using wavetable to describe filter response). It’s equivalent of doing multiplication in frequency domain, the problem with reverbs is that you’ll need to optimize it for using larger IR sizes.

Yeah still working on it when I have some time, which is not very often these days.
Thanks for these ideas, I’m definitely bookmarking your patches repo for further reading too. There’s a Resynthesizer In the fundsp crate which does pretty much what your FFTProcessor class does (I think, it’s not in the repo). I knocked together a basic Vocoder patch using it a while ago.
Maybe the easiest thing to do is clone the fundsp Resynthesizer, and modify it so it uses the CMSIS FFT obtained via the Service Call to do the transformations.

Had a go this evening, it’s going well so far.
One weird thing though - arm_rfft_fast_init_f32 with size 128 doesn’t work! It looks like a specific bug in the CMSIS version that is included in the current firmware - I found this commit which looks like it fixes the issue for more recent versions.

FFT Done! owl_patch - Rust. The audio blocksize seems to hamper things a little bit though - with the default size of 32 samples my resynthesizer glitches with an FFT larger than 256. If I up the blocksize via a midi sysex message to 256, then it’ll happily process with 4096 bands (and matching latency of course).

I think the last major feature is display support. The issue here is going to be not having a device with a display to test it on. It also seems like there’s no way to tell whether the device has a screen, or whether it’s mono / colour, except by checking against the hardware_version field. Only Prism has a colour screen currently right?

Well with BS = 32 you end up running x8 more FFT transforms per second compared to BS = 256, so the glitch is likely just running out of CPU.

“Prism” was a prototype that Martin never released. You can take a peek at it here - https://www.youtube.com/watch?v=dnpo7gqGzhA . So there’s no official hardware with color displays, even though there were plans to have something based on Xibeca board (which is unlikely to be released now). So for the time being only monochrome is relevant, RGB was there for completeness (and to showcase that Prism demo). And in case of C++ patches there’s just one version of display supported.

Here’s what hardware with display actually exists so far:

Manufacturer Device Platform Display Comment
RebelTech Magus OWL2 128x64, BW Long sold out, OWL3 firmware not released but was planned
RebelTech Prism OWL2 128x128, Color Prototype only
RebelTech Genius OWL3 128x64, BW Sold out, I think there wasn’t that much of them built
ElectroSmith DaisyPatch Owlsy 128x64, BW Hardware still available, firmware is my OWL port
KXMX Bluemchen Owlsy 64x32, BW DIY hardware, requires ordering PCB on your own as it’s no longer being sold
IXOX PreenFM3 Custom OWL FW 320x240, Color Hardware is DIY but you can buy it and get a nice metal enclosure. I’ve got FW mostly working, not releasing yet as it needs more fixes for flash storage

Any chances that you’ll manage to access any of them? If RebelTech was still operational, I’m sure Martin would send you something to test with, but that’s not the case unfortunately. And Befaco didn’t make anything with displays (and I’m not sure if they will do anything with OWL after Oneiroi too).

Besides that I’ve made a port of OWL to Axoloti board and it would be fairly easy to enable one of displays that it already supports as it has the necessary pins unused SPI peripherals exposed.

It might be possible to do the same thing with Lich - I haven’t checked pin availability for I2C or SPI yet and this would probably require soldering a few wires to OWL board. I.e. something like this could be converted to an expander module for connecting an I2C display (ignoring their Arduino board and just controlling the display from OWL).

Attach a screen to my Lich? Crazy idea! I’d have to find the right screen, find some spare pins on the board, map them in CubeMX and regenerate the base FW, copy all the custom Lich code across, plus bits from the Magus3 and Genius firmwares, then loads of customisation… no way I’m doing that…

erm…

Very much a PoC so far. All the interface code is taken from Genius, which is built around having two encoders. I’ve chopped out just enough to get it to run, but it will need more work before it actually makes sense.

The FFT / blocksize thing is an issue because, for example, with BS=32 and FFT=1024, with 4 windows, I’m only running the FFT transform once every 1024/4/32=8 blocks, but it takes longer than 32/48k = 0.66ms to complete, so it’s not done in time for the next block. With a BS of 256, I’ll be running the FFT every block, but have 5.33ms to complete it in.

Nice to know I don’t have to worry about colour screens for now though, that will make adding the Rust support a lot easier.

I’m seriously impressed that you’ve got this working. You’ll surely notice that adding a display makes a ton of difference to device usability. This begs for a nice panel (maybe with ports for serial MIDI too). If space is a concern, Bluemchen uses smaller 0.49’’ display with SSD1309 chip controlled over I2C that fits in a 4 HP panel and I’ve got this working on OWL too.

BTW, Lich has a single encoder but does very little with it. Normally on Rebeltech devices 2 of them are used for UI. However I’ve written UI controller for Daisy stuff based on just 1 encoder - https://github.com/antisvin/OpenWare_Owlsy/blob/daisy/DaisyPatch/Inc/DaisyParameterController.hpp . A bit messy as it’s based on older version of UI that was used on OpenWare, but it might be a better starting point than Genius.

As long as you abstract it in a similar fashion to C++ version’s template class for colour buffer, there should be little to worry about. Btw, in case if you’ve missed that, the colour displays would typically use 12 bits for colour, not all 16. I can test your colour buffer support if necessary once you’ll be done with more important stuff.

BTW, with FFT and display you can make a tuner patch that utilizes all that and is very useful to have.

Yes a big difference, this screen will definitely be getting a permanent home in my rack at some point in the future. For now though, it’s good enough to be able to check the Rust screen support.
I found a note somewhere saying the colour screens were configured as RGB565, using all 16 bits, but I’ll double check that before committing anything there.
I was thinking, that since it wasn’t too hard to get the mono screen working, I could just pick up a colour screen and have a go with that. But it seems SEPS114A is not very available, and SSD1331 doesn’t have real support in the OpenWare codebase, only passing mentions. I think colour support will go on the back-burner for now.
Yeah I’ll definitely have a go at a tuner patch I reckon, the scope you’ve got in your firmware looks handy too, I may try adding a patch for that.

You’re totally right, I was thinking about compressing 24 bits of RGB into 16 bits in 565 format. Sorry for this misinformation :wink:

Yes, the supported controllers are obsolete parts and I was searching for them a few years ago without much success. So the only realistic way to have color display on OWL is to build PreenFM3 with my (incomplete) custom firmware. I have an old video here: https://www.youtube.com/watch?v=ydeL-JCYshg

Martin had plans for adding support for making something based on ST7789VW controller which is quite similar to ILI9341 on PFM3, not sure if he went past reading datasheet.

the scope you’ve got in your firmware looks handy too, I may try adding a patch for that

That scope in firmware is mostly for testing inputs when module is unplugged, however it doesn’t allocate a separate buffer for audio as FW memory is very limited. So it peeks at current audio buffer, but knows nothing about audio arriving between video updates. This allows only displaying a small part of audio without syncing to it. With a scope based on patch you don’t have such limitation, but you’ll quickly realize that visualizing a long buffer of audio on a tiny display can be challenging. Especially so if you want to support displaying signals from LFO to audio rate

An alternative is to visualize spectrum, but it won’t fully replace that and is only useful for audio.

Alright screen support is in! Just pushed version 0.4.0 of the crate, with support for Monochrome screens.
That’s the last major feature done, time to start building some more interesting patches