HexoSynth 2022 - Devlog #6 - Workflow and Oscilloscope
The HexoSynth modular synthesizer (programmed in Rust) got an awesome oscilloscope and lots of workflow improvement in the last week.
If you don't want to dig through the detailed log:
TLDR: Skip to the Devlog 6 Conclusion Section
This is the day by day log of my progress. If this is too verbose for your interest, please skip down to the Devlog 6 Conclusion Section.
HexoSynth: Started implementing a very basic sample selector. Currently only the samples in the current working directory are listed. But it allowed to me finally mess around with the integrated sample player. You can listen to some stuff I did in the video section at the end.
HexoDSP: While playing around with the sample player and TsLFO I found a bug in the DSP implementation of the TriSawLFO. It was originally ported from ValleyAudio's ValleyRackFree codebase to Rust and I did not rethink the whole logic. Modulating the reverse point of the LFO now will work properly.
HexoDSP: I also sat down and write up a guide how to implement a new DSP node. Refer to the HexoDSP API Documentation - DSP node implementation guide.
HexoDSP: I've been bug hunting the last hours after hearing some artifacts in the sample player. Also the delay line interpolation is now properly tested and should sound less noisy in the reverb. While I was at it, I refactored the sample player code and the cubic interpolation function.
HexoDSP: Also wrote more documentation for some helper functions/structures in HexoDSP.
HexoSynth: Added more GUI tests for the picker LMB/RMB click workflow.
HexoTK: The DPI factor has been a long standing issue with HexoTK. Before I focus more on GUI features I feel that I need to tackle this problem first. What use is a GUI that is too small on other people monitors or with other people's screen scaling preferences? Anyways, the first step was to tell the layout to layout for smaller/larger DPI factors:
HexoTK: I took the approach to run the GUI layout in physical pixel dimensions.
That means the layout algorithm is no longer based on the logical pixel dimensions.
That worked well as you have seen in the last screenshot from 2022-07-21.
However, the programmer does not know about DPI of the target machine
and specifies the amount of pixels in the HexoTK widget's
Style data structure
in logical pixels.
This means, HexoTK needs to derive the physical pixels amounts from the
data structure when drawing the widget. But that means I have to touch all pieces
of drawing code in HexoTK and make it DPI aware. I tried doing it the easiest
way first by just applying a DPI factor to all the sizes when I draw.
I saw this was going the right direction, see the next screenshot:
This told me, that the approach works and I sat out to improve the code.
I did not want to litter my drawing ode with a
dpi_factor all over the place,
with the risk of forgetting to apply the factor. So I sat down and thought
about an API improvement. The idea was to wrap my current
Style data structure
and apply the DPI factor on the fly when accessing it from the drawing code:
The resulting code looks much cleaner and more maintainable. So the next
task is to touch every piece of drawing code in HexoTK and make it DPI aware
using this new
Anyways, some hours later I was done with the DPI factor conversion of the GUI drawing code. Now the GUI is freely scalable. I don't have a good test setup here to check if it works with a real 4k display. And the fractional scaling on Ubuntu seems to act weird on my computer. So I will leave it at that for now, until either I or someone else can test it. At least I got the prerequisites sorted out now to ultimately do proper DPI scaling.
HexoSynth: I improved the movement of a single cell with the right mouse button. The operation will now try to keep the connection to the adjacent cell(s) alive.
HexoSynth: A while later I added the DSP chain split mouse gesture/functionality. By dragging with the right mouse button between adjacent cells, you can split up a chain of hex cells and push one part away. This allows you to easily insert new nodes anywhere you want.
HexoSynth: By dragging from an empty cell to a cell with a node you can create copies and new instances of the same type. You can also drag a cell to a more distant (non adjacent) cell for creating a linked copy or a new instance right at the destination.
HexoDSP: I greatly simplified building simple DSP chains for the hexagonal Matrix API today. This improves future test generation and also allows refactoring DSP chain placement in HexoSynth. With the MatrixCellChain builder API this rather complicated direct setup of hexagonal cell tiles:
let sin = NodeId::Sin(0); let amp = NodeId::Amp(0); let out = NodeId::Out(0); matrix.place(0, 0, Cell::empty(sin) .out(None, None, sin.out("sig"))); matrix.place(0, 1, Cell::empty(amp) .input(amp.inp("inp"), None, None) .out(None, None, amp.out("sig"))); matrix.place(0, 2, Cell::empty(out) .input(out.inp("inp"), None, None)); matrix.sync().unwrap();
Becomes much easier and straight forward to understand:
let mut matrix = Matrix::new(node_conf, 3, 3); let mut chain = MatrixCellChain::new(CellDir::B); chain.node_out("sin", "sig") .node_io("amp", "inp", "sig") .set_atom("gain", SAtom::param(0.25)) .node_inp("out", "ch1") .place(&mut matrix, 0, 0); matrix.sync().unwrap();
HexoSynth: Next step was to integrate the new
MatrixCellChain API into HexoSynth.
That worked very well. The
MatrixCellChain API also allowed proper error reporting,
which improved creation and maintenance of the GUI test suite.
HexoTK: Implemented very basic image loading in HexoTK. Still need to make it load images from memory though. But the WichText widget can now draw images:
HexoSynth: The test suite now covers all the important hex matrix editing gestures.
HexoSynth: Added more actions to the context menu today too:
HexoSynth: One last addition for the day was the "Remove Chain" operation and a confirmation dialog for the destructive action.
HexoSynth: This morning I added multiple sources of random/unexpected inspiration. You can add random input/output nodes to an existing node or create completely random collections of nodes. The idea is to inspire you to experiment around, find interesting combinations. What about a challenge: Of using 6 randomly generated nodes to make an interesting soundscape?
HexoDSP: As preparation for an oscilloscope view in the GUI I added a special Scope DSP node that captures a portion of it's input signal and writes it to a shared buffer.
HexoSynth: A few hours later, and I got the first prototype of the oscilloscope working:
You can actually have up to 3 signals being displayed in the same oscilloscope:
Next I added styling API for the graphs. And then you see how important a bit of color and contrast can be:
HexoSynth: After styling the oscilloscope I enhanced the layout today and moved it into the panel with the pattern editor. A button for toggling it's size has been added too.
HexoSynth / HexoTK: Next I added labels to the oscilloscope, for displaying the min/max and range between min and max:
HexoDSP: One step further and I was able to assign more parameters to the Scope node. Such as the time setting, which allows you to zoom into the sample recording of the oscilloscope. The time setting goes from 0.1 millseconds up to 300 seconds (5 minutes) of scaling. Of course, at that scale we are seriously undersampling the signal, which is only useful if you want to look at long term CV signals anyways.
HexoDSP/HexoTK/HexoSynth: I've further peeked into VCV Rack version 2 source code of the Scope module. It improved waveform drawing a lot since version 1 of VCV Rack. The waveform there is now drawn using the min/max envelope of the signal. This has multiple advantages actually. It gives a more realistic view of the source signal, as all maximums and minimums are recorded properly. This allows not only to draw a nice waveform in the scope, but also displays the exact values in the labels properly!
YouTube Sound Demos
Sample player shenanigans after I got a prototyped sample selector working again:
Sounds kinda scary :-)
Devlog 6 Conclusion
This week and a few extra days has been a ride through all depths of HexoSynth' code. First the slow start with the super basic sample selector, based on my overpowered text widget (WichText). Had some fun with the sampler afterwards.
I worked a lot on the UI workflows this week. All matrix editing operations from the HexoSynth 2021 are back now. You can move whole chains of cells. You can delete chains. You can split chains, you can move around individual cells. And you can even rotate connected cells around it's source/destination cell. Most of these operations even are covered by automated GUI tests by now.
There are also more context menu functions now. You can remove unused port assignment and even generate new random nodes for inspiration.
I also went back the core of the GUI library HexoTK and implemented DPI based GUI scaling. I was not able to properly test it yet, but it's prerequisites are implemented once I or someone else can test that.
As the big changes on the GUI more or less came to an end, I wanted to tackle another big new feature for HexoSynth. I wanted a larger scale signal oscilloscope to inspect the signals in greater detail. The new scope node allows exactly that now. And the fundamentals of it are implemented well now after two days. There are still some little features and automated tests missing for it though.
Next up are the following tasks on my list:
- Finish oscilloscope implementation, there are still tests to add and little things to implement.
- Add more automated test cases for the UI workflow.
- Maybe, just maybe focus on WBlockDSP again. The idea of small contained JITed DSP nodes still got a hold on me. And I kind of want to tackle all big things early on.
- 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.
- Finish the nih-plug integration. That means a better integration of HexoSynth as VST3/CLAP plugin into your favorite DAW.
- Add back editing CV widgets.
In case you find this project interesting or have questions,
you can reach me via Discord these days, check out the
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:#email@example.com.