Skip to content

Instantly share code, notes, and snippets.

@bobbicodes
Created September 11, 2024 15:59
Show Gist options
  • Save bobbicodes/3c60e2bfaf3d4c8b5313fd24a9ea3531 to your computer and use it in GitHub Desktop.
Save bobbicodes/3c60e2bfaf3d4c8b5313fd24a9ea3531 to your computer and use it in GitHub Desktop.
Lisp-MML Language Guide
  • Gentle intro to Lisp-MML syntax

  • Lisp lists

  • Lisp is characterized by its use of parenthesized lists and prefix notation:
  • (+ 1 2 3 4)
  • Prefix notation means the first element of the list is the operator, which is the + function in the expression above.
  • The Lisp dialect used by Lisp-MML is based on the Clojure language, which supplements these lists with other built-in data structure literals, namely vectors (array-like, sequential collections) surrounded with square brackets ([]), and maps (key/value pairs) surrounded with curly braces ({}).
  • [5 4 3 2 1]   ;; nested vectors with numbers as elements
    
    {:length 12
     :pitch 60} ;; map of 2 keys, each with an integer value
  • :length and :pitch are keywords, which are similar to strings but are preceded by a colon instead of surrounded by double quotes, and are used for keys in maps. In the context of Lisp-MML, our song itself is sent to the compiler in the form of a single map, when passed to the play function:
  • (play
      {:square1 [{:length 12 :pitch 50 :volume 5}
                 {:length 24 :pitch 52}]})
  • We can break this down because it contains several core language features.
  • (play) is a function call, we know because it is in a list (in parens). This is a nice bit of syntactical consistency gained as a result of our choice of data literals - if it's in a list (()) it's a function call, if it's in a vector ([]) it is sequential data.
  • The play function takes a map of our music data, with each channel having its own key. Possible keys are square1, square2, triangle, noise, p1, p2 (VRC6 pulses), and saw.
  • The value mapped to each key is the music data for that channel, represented as a sequence of maps, each map being a note or other command, like a volume or duty change.
  • volume and duty keys can take either a fixed integer value or a sequence. Volume and duty envelopes do not cycle, they stay on the last value.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment