The Programs of the Week I Had Jury Duty
This Week’s Program: Apr 10 - Apr 14
This week, I fulfilled my civic obligation. I was selected as an alternate juror in a civil case that ended up being settled out of court. I feel proud knowing that I played my part in the administration of justice, and prouder having committed code from the jury assembly room in the New York County Supreme Court Building.
DUN DUN
Over the weekend, when I normally do not like to program, I did some programming. Or at least, I did some programming with Automator and Workflow — two tools that don’t involve code but nevertheless compose program parts together into larger programs. I love these tools. In Automator I made two macOS Services. Both are useful front-ends to the Keybase command line interface for signing and verifying textual messages. You can find both of these in my Keybase public folder. See my toot on Mastodon for some additional context and an animated gif of the signing Service in action. In Workflow, I made a thing so that I can subscribe to RSS feeds in Feed Wrangler quickly on my iPad. I’ll share that out if anyone is interested. Mastodon has given me some good old school web vibes and inspired me to stitch these small programs together.
Let’s talk about Overscan.
Overscan
I think the last few letters of mine have been difficult to follow. This project is a study in Yak Shaving.
Here’s a review:
- I am learning the Racket programming
language. This will be my third Lisp (after learning just enough
Emacs Lisp to be productive and
building
sonic-sketches
in Clojure). - The project I’m writing in Racket is
overscan
, a live-coding environment for broadcasting video. The idea is to build an environment like Overtone or Extempore but focused on streaming live video to platforms like Twitch or Facebook Live: evaluate a sexp and change the broadcast. - To start, I’m going to be writing some Racket bindings to the GStreamer multimedia framework, written in C and GLib.
- Instead of writing a bunch of FFI bindings manually for GStreamer (aka GST), I decided to use GObject Introspection (aka GIR). GIR provides a bridge between a GLib library and another language by exposing an API to read metadata from the library and dynamically construct bindings.
- There already is a GIR package for Racket, but it hasn’t been updated or maintained in nearly a year, and I’ve found numerous inconsistent and buggy behaviors with it. I’ve decided to write my own GIR bindings in Racket to use as a basis for my GST integration. That’s where I am now.
So now you’re caught up on what exactly I’m doing with all this Racket↔C interop. Soon, I’ll pop out of this level of the stack and get to GStreamer and maybe actually producing a video. I’ve cleared a lot of the way through my GObject Introspection bindings and have an API that I’m feeling better about and refining along the way.
The last real major hurdle to go here is to represent the most relevant binding: bindings for GObjects. Once those are in place I will be able to move on to integrating GStreamer.
Here are some highlights…
acd9682ad477723de02c11a208314903dac5b8fb
I create a new struct, gtype-instance
, which represent C pointers to
GTypes. Racket’s FFI library has some ingenious ways of dealing with C
values, and my favorite is the cpointer
construct. These
represent pointers that have a tag associated with them, and when
going from Racket to C, if that tag is not present Racket will error
out. When creating C Types with Racket FFI, you provide two functions:
A Racket → C function that will coerce a Racket value into the
appropriate representation, and a C → Racket function that takes that
representation and returns a suitable value.
So what I’ve done here is create a C Type that will be converted into
an instance of gtype-instance
when used within Racket, but will be
coerced back into a pointer for use in C. It’s really easy to coerce
back into a pointer form, because Racket structs can behave like C
pointers when they have the prop:cpointer
property.
a9092e219acb4230981f76cf0016df4e1950da56
This is exploited here, where I create a sub-struct of
gtype-instance
, gstruct-instance
. This is effectively a decorator
around a pointer to a C Struct, but when used in Racket you can
observe the fields as a list. gstruct-instance
also behaves like a
procedure, and so you can call it like one to invoke methods on the
struct.
d3cdba396060e83b971852ebb9cb7f0b0f1159e1
Enums
are the next C type to get bindings. When used in C, these are
effectively longs
. When coerced into Racket, that long
is used to
lookup the symbol value. So when you call a C function that returns an
Enum, what you get back is a symbol of the value of that enum! Making
these C Types within Racket is neat!
Also, see that git commit message?
I’m committing this from the NY County Supreme Court because I’m in Jury Duty!
I am a cool dude.
There’s a bunch more commits but they’re iterations on a lot of the
concepts I’ve laid out already. I make sure that type information and
conversions are conserved when converting from one of my home-rolled C
Types to the types that are represented in the Type Union used when
invoking functions. I move the file into its final position:
ffi/unsafe/introspection.rkt
. I create an abstraction for something
that occurs again and again in GIR, building up a list of items from
two functions: one that returns the number of items and the other that
returns the item at a given index:
(define (gi-build-list info numproc getter)
(build-list (numproc info)
(curry getter info)))
I make sure that gi-base-info
base info values are always
deallocated appropriately. And finally I convert the overscan package
to be a “multi” collection in Racket’s package management parlance.
Next week, GObjects and signals, API hardening, and documentation.
👩⚖️ Mark