Home / Blog Menu ↓

Blog: #cli

A year and a half ago I started working on a REPL-based music composition environment called Trill. After a short amount of time I stashed the project for the time being, but since I can see myself working on it again someday, I figured it’s due a write-up.

The core idea here is a text-based REPL for composing music (and by music I mean more things like hymns and movie scores and folk songs, not as much pop or rock or electronic), with a focus on making the composition experience more aural and less visual.

An example session will hopefully help anchor the ideas:

> score mysong
> staff piano             # add a piano staff
> keysig c
> timesig 4/4
> keytime c 4/4           # alternate
> play v. v. v. iii....   # plays the note sequence (. = quarter note, .. = half note, .... = whole note)
> play v. v. v. iii-....  # - = flat (and v, iii are based on the key signature)
> play v/ v// v///        # eighth, sixteenth, thirty-second notes
> add .                   # adds what was last played to the active staff
> play V IV^ IV_          # play a V chord and then a IV chord one octave up and again one octave down
> pm vi.                  # plays the last measure plus whatever notes are specified
> add vi.
> staff violin            # adds a violin staff
> play @arpeggiate piano  # plays two measures of violin arpeggiation based on the piano staff (where @arpeggiate is a generative method)
> save

And some miscellaneous, unordered notes:

  • Rather than seeing the notes listed out (either in standard music notation or in text format), you basically only hear them (via play). This is the aural-over-visual part.
  • Duration is represented by the number of periods (cf. the play examples), as an experiment with making the length feel more visceral — a longer string of periods makes for a longer sound.
  • I’m also experimenting with using the relative scale notes (the Roman numeral notation) rather than absolute note names (C, D, E, etc.), to make transposing easier.
  • Not sure yet how dotted notes fit in here.
  • I threw in the idea of having some kind of generative functionality (@arpeggiate), but that’s pretty raw and not thought through at all yet.
  • The session transcript would also possibly function as the source for a song, and reloading it later would just skip the actual playing and instead just build the staff. Kind of nice to have the full history recorded, I think.
  • Influences that I’m aware of: ABC notation, Lilypond, and Alda.

To be clear, I have no idea if any of these ideas are actually good. They’re just half-baked thoughts at this point. I did implement a very small proof-of-concept using FluidSynth and Prompt Toolkit, with the play functionality working, but that’s where I left off. (Writing about it now, though, has me excited again. Maybe this will be my homework-avoidance project for the semester.)

The main things I need to sort out when next I work on Trill are how to navigate a score and how to manipulate notes using textual commands and this aural-first system. Basically, some way to say “go to this part and play this much” and “bump this note up this much” or “make this note a chord.” Seems doable; I just haven’t gotten that far yet.

Reply via email or office hours

Vinci intro

Another entry in the snore-inducing series talking about my personal productivity tools.

Vinci is my journal/log app, a private blog of sorts. It’s a Python app running in Django.


Vinci has notebooks which contain entries. Like most blogs, entries are displayed in reverse chronological order. It looks like this, except I usually write in English, har har:


Editing an entry is a modal fullscreen panel, with the main textbox at top, the metadata textbox under that, and some controls at the bottom.


On save, Vinci splices the metadata and the text together and runs it through the payload processor.

Payload syntax

Vinci uses the text-based payload idea like Liszt. Its payload syntax looks like this:

:tags foo, bar

Worked on the [foo project](/leaf/3290) for a while. Ran into a few issues.

The first line is the notebook specifier. The second line (and this could have been anywhere, didn’t have to be at the top) has a command with some parameters. And the rest is Markdown.

There’s also a variant syntax where the first line can look like this: /projects/tag/tag2/tag3. I’ve started using that a little more often.

Because of how I like to write in my journal, I’ve also set up a special case for my /journal notebook, where adding an entry will either create a new entry for the day (if there isn’t one yet) or append to the existing entry, so there’s just one entry for each day. (I use Gate or Quill to jot down a paragraph and then append it quickly and easily.)

How I use Vinci

I use Vinci a lot. I maintain my personal journal in it, along with logs for most areas of my life — work, school, writing, projects, church, etc. Sometimes I create notebooks for specific projects, other times I use a higher-level notebook (like /projects) and use tags instead. At some point I’ll probably consolidate.

I have a /thinking notebook where once each morning I think through my current tasks/projects and write out what I need to do for each. Writing things down makes a world of difference for me, across the board.

Each morning I also spend a few minutes reading one of my past journal entries, as mentioned earlier today. (A while back I scanned all my paper journals and I’ve been slowly transcribing them — we’ll get to Ditto soon — and importing them into Vinci.) Lately I’ve been reading through my 2004 college entries. My undergraduate years were great, but I am very, very glad I’m not in that phase of life anymore.

Lastly, I reference these notebooks fairly frequently. (When did we replace our dishwasher, what did I last work on for that project I haven’t worked on in months, etc.)

The future

Vinci currently uses Whoosh for full-text indexing, but it’s unsupported and hasn’t been working as well for me lately. Several months ago I realized that if all my notebook entries are stored in plain text files, I can just use ripgrep or ag for fast and accurate searching, with the further benefit that in the event of my untimely demise, everything would be fairly easy for my family to copy out and preserve. (I really like plain text, can you tell?)

To that end, I started writing a new, smaller app in Go called Leaf. It’s going well, but I’m tempted to switch to FastAPI. Not sure yet if I will or not. (I’ve enjoyed learning Go and have used it on several small projects now, but I’m also thinking more about long-term maintenance across all these apps, and using a single stack would simplify things for me.)

I’m also thinking about adding a small CLI (in the web interface) that would make entry management easier — moving all entries with a specific tag to their own notebook, for example. Truth be told, I don’t know that entry management happens frequently enough to warrant a CLI, but I’m intrigued by the idea of putting a CLI in the interface, and if it goes as well as I hope it will, I see myself doing that in more of my tools. (Right now I see it as an extension of the search interface. Where right now I type dishwasher to search for that keyword, I’d eventually also be able to type something like :move /projects#cardiff /cardiff.)

As with Liszt, I’m also looking forward to moving to a lighter, simpler codebase. Vinci has a moderate amount of vestigial functionality that needs to go.

Reply via email or office hours