February

double densed uncounthest hour of allbleakest age with a bad of wind and a barrel of rain

double densed uncounthest hour of allbleakest age with a bad of wind and a barrel of rain is an in-progress piece for resonators and brass. I’m keeping a composition log here as I work on it.

There are sure to be many detours. Getting it in shape might involve:

Sunday February 25th

Valgrind yells at me: go fix your program!

It has been a bit of a memory management weekend. I tried out clang’s Address Sanitizer for the first time, which pretty much immediatly halted execution telling me my program is attempting to allocate terrabytes of memory. Um!

I haven’t solved that mystery yet. There are no such giant allocations after some frustrated double-checking, but I think it might be seeing a leak and peering into the future where it sees a gigantic cumulative total of unfreed allocations?

In the process I found a number of places missing checks for NULL pointers or forgetting to initialize memory before sending it off somewhere else. There’s still one instance of uninitialized memory valgrind has been shaming me with – “go fix your program!”. I haven’t been able to track it down yet.

At some point during this spring cleaning the stack smashing bug in my test program went away. I’m not sure when! It only occurred to me this morning that I didn’t remember seeing it recently. There was an instance of the program running overnight (a practical test for memory leaks, why not) so I sent it a shutdown message and hey indeed, it no longer barfs.

There’s ongoing dropouts unless I increase the jack block size to more than 1024 frames. The test program runs smoothly with a larger block size (somewhere between ~2000 and ~4000 frames, I haven’t found the edge) but there’s only 20 pulsar oscs running in it. Doesn’t seem like that’s enough computation to cause dropouts from just running out of time trying to compute a block of samples, but… there it is.

Saturday February 17th

It’s pledge season again!

Edit: sorry to anyone following this RSS feed, I’m tweaking the GUID field structure today to support breaking cache on edits, but this probably also created copies of old entries in your reader! Apologies.

Async rendering in the new astrid instrument toolkit is working again! I wasn’t setting the instrument channel count early enough so the scheduler thought there were always 0 channels and happly chugged along mixing every frame and copying it to… no channels, lol.

Anyway, instruments can now optionally define a renderer callback that looks like this:

lpbuffer_t * renderer_callback(lpinstrument_t * instrument) {
    lpbuffer_t * out;

    syslog(LOG_DEBUG, "Reading soundfile in renderer callback...\n");
    out = LPSoundFile.read("chimeC.wav");

    syslog(LOG_DEBUG, "done reading got %d frames and %d channels...\n", (int)out->length, out->channels);
    return out;
}

That just reads a chime sound from disk and returns a pointer to the buffer. At that point the scheduler owns the memory and will lazily free it (if the cleanup routine is run!) when the event has finished playing and has been moved back into the event nursery.

This doesn’t bring astrid C instruments up to speed with their python versions yet. There are still a number of interfaces I want to expose in the astrid api that are currently contained just in the cython renderer.

Once that’s better sorted, I can work on optionally embedding the cython renderer again. What I’d really like is to keep these renderers mostly agnostic to their environments. Meaning they are either mixed into the instrument output by adding a pointer directly to the scheduler from the C context, or they are serialized and sent over the buffer queue like the previous generation of astrid python instruments do. That opens up writing instruments in any language or environment as long as there is a proper serializer for the buffer…

Anyway, the chime sound is the async renderer and the rest is the new stream-based pulsar instrument I’ve been using as a test bed to work on the rain system:

Thursday February 15th

Fixed some bugs in the serial listener program before work today and hey: button mashing!

Tuesday February 13th

To get astrid set up to test external serial communication, this morning I realized I need to be able to embed some core types in an arduino sketch. libpippi is meant to be embedded in other programs but also in firmware and arduino sketches and so on. There’s a relatively lightweight pippicore.h for this, with other pieces available (like mir and microsound and ugens and special oscs etc) to be included on-demand to keep firmware sizes small, but in this case I really just want to be able to embed some of libpippi’s internal types to serialize messages on the arduino side.

To start I split out all the internal constants in libpippi’s core into its own header, and the core datatypes into another. Factory datatypes are still in the main pippicore.h header for now because I don’t think they are really needed. If you want the factory types you want to use their implementations, too. Better to just include the whole pippicore.h header in that case.

I thought I’d have to do some special packaging for arduino to embed it, but after breaking the headers out like this it’s easy to just have my makefile copy the latest constants/types headers from pippi into the sketch directory before building it with arduino-cli.

Now there’s an lpmsg_t struct in my sequencer sketch. Yay! :-)

The LMDB stack smashing bug is still eluding me. I did some cleanup after work yesterday, trying to tidy up some potential issues with my usage of jack, since I was suspicious the issue was related to the jack callback trying to interact with an LMDB database that had already been closed, but it doesn’t seem like that’s the issue.

Sunday February 11th

I’m consolidating some of the astrid setup into one instrument_start function that sets up the LMDB session, configures and starts the jack callback, and opens the message queue. I think the internal message seq will be part of that base instrument setup, and that async renders will be opt-in? Makes some more boilerplate for basic C instruments, but I’ll probably be calling this all from python at some point, too? So I could hide the boilerplate a bit more at that stage, probably.

Anyway, apparently I introduced some crazy bug doing a little refactoring just now. It’s my first stack smashing error for pippi or astrid, I think lol. (Is that a milestone of some kind? Haha.)

After the program is done cleaning up, on exiting it barfs:

(~/code/pippi/astrid) % astrid-pulsar                                                                                                                                                 Sun 11 12:16PM  lake 
stacklength 8192
Waiting for messages...
Waiting for messages...
Waiting for messages...
Cleaning up...
Done!
*** stack smashing detected ***: terminated
zsh: IOT instruction (core dumped)  astrid-pulsar

I just discovered today that coredumpctl will drop you into gdb no questions asked with just coredumpctl debug. Nice! Saves dumping the file and then running gdb manually. Small thing, but still nice. Maybe I’ll stop leaving core dump files all over the place too now. :-p

Saturday February 10th

Alright no more dropouts! I just needed to increase the jack buffer size to 4096 I guess, but I still haven’t done any profiling so who knows. :-)

Here’s a bash one-liner simulating an external serial trigger sending an update message (with no actual message payload, the instrument is set up to respond to all update messages the same way for testing) to the pulsar test program, changing the frequency of the 20 oscillators every second.

It’s not terribly exciting, musically, but I’m really glad this is working so well. Getting to the fun stuff fairly soon: serial I/O, resonator design and even writing some notes…

rain is partly an excuse to play music with my uncle, who is a really great jazz sax player. I haven’t started writing the themes yet, but I want to include some simple fixed trumpet/sax duo passages along with sections that are improvisational & aleatoric for trombone & sax. I have some hope of improvising with the trombone. I’m finding that I really enjoy playing it? I don’t play very often, and until recently only for myself, just for fun. I’m looking forward to exploring the rain system and tip-toeing back into tromboning in the world again, though.

Update: I thought I’d do more plumbing, but just tried hooking the pulsar script up to the ADC again and now it actually feels kind of like something… just ring modulating the osc output with the mic input is already fun to play around with.

I haven’t figured out how pitch input is going to work. It’ll probably be some combination of pattern recall and freeform control. I’ll probably prototype with a MIDI keybaord at some point, but what I’d like to work out is refining the command inputs I’ve used with astrid in the past, and pairing that with some physical controls. Like a programmable keyboard, not-a-keyboard.

I was marveling at the timbre controls for the Electric Sackbutt the other day. I wonder if something like arcade buttons with capacitive touch surfaces for a second control dimension could be possible…? Donno.

Thursday February 8th

I’ll invoke the brain fog card, but I don’t think I really have long covid… it’s just been one of those weeks I guess. (Months?)

Still, it’s a relief to discover after work today that yes indeed I was doing something very silly with LMDB, I don’t think I was experiencing a read bottleneck after all. Thank goodness, because I’m really excited about LMDB.

The silly thing I was doing was storing a reference to the transaction on the instrument struct, and using the same reference in the main thread for writing to the database. One transaction to rule them all. Meaning in the main thread the program was using the same transaction handle to write as the audio callback thread was using to read. I’m surprised that even worked, but I think I must have created an insane situation in the transaction and the callback was just exiting early sometimes, causing the dropouts I thought were xruns.

I’m not totally sure though. Maybe stopping to wire up some more concrete profiling makes sense.

I kinda want to get the serial bridge working so I can play with my arcade buttons tho.

Yep. I’m still hearing dropouts after switching to using one transaction for reads and one for writes. I’m sharing the environment. That is OK. I’m also using two different database handles: one for reads and one for writes. I’m not sure that’s right.

It’s confusing: the docs say once a transaction has been started and a database has been opened inside of it, the database handle can be reused for future transactions. The docs also say that you must either commit your transaction or abort it (or refresh it) all of which close the database, too.

So I’m confused because I’m not reopening the database in new transactions, but supposedly every time I commit or abort the database closes?

I’m also opening the database twice in one process: once for reading only and once for writing only. I don’t know if that’s bad. The docs say don’t open more than one environment in a single process because the POSIX file locks will go goofy, but I’m not sure if opening the database once for each transaction is bad too?? Or if I ought to be reopening it after it closes on each transaction completion??

Something is still goofy anyway. I’m hearing dropouts again. This all reminds me of printing, actually.

The booklets I’m making for the audiobulb comp have two layers of ink: laser print base in black (complete with bespoke printer drum streaking from my 20+ year old printer) and then a layer in various colors of paint pens drawn on with the plotter. I drew the plotter layer over the printed base in inkscape (after a failed round of drawing on vellum and scanning – automatic centerline tracing of the scanned line drawing never looked good) knowing that the actual plot would never line up exactly with the laser-printed layer underneath. The printing term for this is registration: lining things up so when you print the next color, it lines up with the others.

For the booklets it was nice to build around the loose registration: every copy is positioned slightly differently, but the tolerance for the positioning moving akew is pretty high, and the booklet is readable / functional / looks good within the full range of askew tolerances.

For now, I’m going to roll with the misaligned registration of LMDB dropouts because it works aesthetically for rain. The voices that I want to use the streaming interfaces for are OK for dropouts: they’re already meant to be rough and digital in some sense since they’ll be pushed through transducers through materials like wood and metal and those sharp transients on the digital artifacts will become something else.

Wednesday February 7th

I’m dragging my feet on this LMDB implementation, but tonight got things far along enough to send a param update to a running astrid program. It’s read from the LMDB database inside the audio callback. Updates are received via the instrument’s message handling thread when a param update message comes through the queue.

I’m getting xruns with pipewire-jack and LMDB though. I have no reason to blame pipewire, but I am a bit curious if using the native jack backend will make any difference. I tried starting the test program setting the latency explictly with PIPEWIRE_LATENCY="1024/48000" astrid-pulsar but was getting xruns even then sometimes.

LMDB reads shouldn’t block, but disabling the reads in the audio callback makes the xruns go away, so that’s not super encouraging. I’ll have to spend some more time with the LMDB docs. The documentation is very good, but the abstractions are kind of confusing in the way that environments and transactions and databases and cursors nest. I’m hoping that I’m using it wrong. Otherwise… maybe try a local memory cache and use another thread to do slower reads? I’m not sure.

Still, it was exciting to be able to interact with a stream-based astrid instrument for the first time. Wiring up serial input and output seems like a good next step for this project… getting closer to being able to start building this test program into something more specifically for rain.

Sunday February 4th

There are some inspiring thoughts in Paul Davis’s recent post about open source and Ardour that are nice to think about while I’m slogging through the current stage of the move to libpippi / C astrid.

I wonder if going forward, large-scale apps like Ardour ought to (as Reaper did relatively early in its life) consider the “script extension system” to be a vital and critical part of the application infrastructure. This would mean, for example, writing large parts of “core functionality” using this system, rather than dropping back into C++ to get things done. There are precedents for this: GNU Emacs, for example, is at some level written in C, but almost everything about the program is actually constructed in Emacs Lisp, its own “scripting extension”. The C core of Emacs is so small and so irrelevant that it almost doesn’t matter that it is there: if you want to modify or extend Emacs, you (almost always) write Lisp, not C.

Pippi and astrid have somewhat different concerns than a monolithic application like Ardour or Reaper, but in 2016 after a major cython refactor I found myself really wanting to be able to embed parts of pippi into other places, or use it alongside other systems, and finding it all basically trapped inside cython. The move to getting all the fun stuff into libpippi where it can go frolic among the microcontrollers or spend a summer inside a Pure Data object was inspired by that realization that I’d put all my eggs in the python basket. (It didn’t help that at the time python was in the worst throes of the v3 situation.)

The side effect is I’ve fallen in love with C all over again, and I’m approaching libpippi and astrid a bit defensively more as a toolkit to build other tools ad hoc, if I can manage that.

I also think about programs like git, which isn’t really just one program, but lots of little programs that can be composed together. UNIX itself is like this with complementary userspace programs that expose their library functions to shell scripts. The program mkdir and the syscall mkdir for example do (basically) the same things. The mkdir program ultimately just calls mkdir(). So does the printf program and others like it.

Astrid has a small set of programs inspired by this tradition, too. There’s astrid-dac which waits for serialized buffers on a message queue, schedules their playback and mixes them to speakers. The astrid-adc program writes input into a shared circular buffer. The astrid-msg program turns command line arguments into binary messages and sends them over astrid message queues. The astrid-seq program takes those binary messages and schedules them for broadcast at a later time. The astrid-renderer program listens for messages on its queue, does async renders and then shuffles them off to the serialized buffer queue.. and so on. These are all in some stage of already existing more-or-less as their equiv library functions, or are on deck to be ported…

Seems useful. Slog on.

Saturday February 3rd

I started adding LMDB to astrid last week. It’ll replace redis as a session store. It might also replace the redis buffer queue, too. I’d prefer that rendered buffers were available for any other process to sample from if they feel like it. Probably the same backend can be used for the internal sampler too, I’d think.

LMDB has some nice features for my use case:

Those last bits just make me feel satisfied it’s probably not going away soon and might remain fairly API-stable.

As a side-effect, I’m finally getting around to placing some of astrid’s temp files into XDG directories. At the momennt it’s just dumping everything into /tmp lol.

To start, I’m putting all the LMDB databases into the XDG data directory (XDG_DATA_HOME) inside an astrid-instruments directory. Each instrument creates its own directory named after itself. On my system that ends up being $HOME/.local/share/astrid-instruments/pulsar for the pulsar instrument I’m working on.

Thursday February 1st

Quick note-to-self to disable touch gestures on a linux wacom tablet:

(~) % xsetwacom --list devices                                                             
Wacom Intuos Pro L Pad pad          id: 8   type: PAD       
Wacom Intuos Pro L Pen stylus       id: 9   type: STYLUS    
Wacom Intuos Pro L Pen eraser       id: 10  type: ERASER    
Wacom Intuos Pro L Finger touch     id: 17  type: TOUCH     
(~) % xsetwacom --set 17 Touch off                                                         
(~) % xsetwacom --get 17 Touch                                                             
off

Log January 2024

Log December 2023