Workbench

Tangling Org Mode Properties Into Source Code Blocks

A problem I've been trying to solve on and off for a number of years is: I'd like to be able to consistently generate multiple artifacts (PDF, web graphics, MIDI) from a single Lilypond arrangement and collect multiple arrangements into a single document (using lilypond-book, for example.)

Lilypond supports this up to a point. You can assign chunks of notation to variables and then define multiple \score blocks that re-use them, but I always find myself having to refresh my memory for things like unfolding repeats for MIDI output or transposing an entire score. Things get verbose and repetitive, and not unlike old-school static HTML web sites where tweaking the appearance of something means editing every single page because they all have quirks and inconsistencies.

Enter Org mode, a Swiss-army knife authoring environment/document format that can do all kinds of things. One of its most interesting features is the ability to evaluate/interpolate/export blocks of source code within an Org mode document. Using noweb reference syntax you can insert blocks of code into other blocks of code... in my case I am hoping I can come up with an Org mode template/workflow that will let me focus on just the actual content of arrangements, and use org-babel-tangle to generate lilypond files for different targets (PDF/svg/transposition/MIDI/lytex).

The first problem I needed to solve was being able to reuse properties across source code blocks, and I got it working:

# -*- mode: org; org-confirm-babel-evaluate: nil; -*-
# -*- mode: org; org-use-property-inheritance: t; -*-
:PROPERTIES:
:TITLE: The Quick Brown Fox
:COMPOSER: Arthur \"Two-Sheds"\ Jackson
:COPYRIGHT: 1969
:END:

* Lilypond

#+begin_src lilypond :tangle quick-brown-fox.ly :noweb yes :padline no
  version "2.24"

  \header {
   title = "<<get("TITLE")>>"
   composer = "<<get("COMPOSER")>>"
   copyright = "<<get("COPYRIGHT")>>"
  }

  \score relative c' {
    c d e f
  }
  \layout()
#+end_src

* Markdown

#+begin_src markdown :tangle quick-brown-fox.md :noweb yes :padline no
  # <<get("TITLE")>>

  ## By <<get("COMPOSER")>>, <<get("COPYRIGHT")>>

  Lorem ipsum dolor sit amet
#+end_src


# Helper function that can be used to insert org-mode properties into source blocks:
#+NAME: get
#+begin_src elisp :var prop="title" :results value :exports none
   (prin1 (org-entry-get (point) prop 'selective))
#+end_src 

Running org-babel-tangle on this file generates two new files:

quick-brown-fox.ly

version "2.24"

\header {
 title = "The Quick Brown Fox"
 composer = "Arthur \"Two-Sheds"\ Jackson"
 copyright = "1969"
}

\score relative c' {
  c d e f
}
\layout()

and

quick-brown-fox.md

# The Quick Brown Fox

## By Arthur \"Two-Sheds"\ Jackson, 1969

Lorem ipsum dolor sit amet

This is a minimal and not super-useful example but it has promise.


Wed May 14 2025 20:00:00 GMT-0400 (Eastern Daylight Time)