lilypond-auto-insert

Comprehensive auto-insert for LilyPond
git clone https://git.jamzattack.xyz/lilypond-auto-insert
Log | Files | Refs | LICENSE

lilypond-auto-insert.el (16020B)


      1 ;;; lilypond-auto-insert.el --- Comprehensive auto-insert for LilyPond  -*- lexical-binding: t; -*-
      2 
      3 ;; Copyright (C) 2020  Jamie Beardslee
      4 
      5 ;; Author: Jamie Beardslee <jdb@jamzattack.xyz>
      6 ;; URL: https://git.jamzattack.xyz/lilypond-auto-insert
      7 ;; Keywords: abbrev, lilypond, music, typesetting, convenience
      8 ;; Version: 2020.11.30
      9 ;; Package-Requires: ((emacs "24.4"))
     10 
     11 ;; This program is free software; you can redistribute it and/or modify
     12 ;; it under the terms of the GNU General Public License as published by
     13 ;; the Free Software Foundation, either version 3 of the License, or
     14 ;; (at your option) any later version.
     15 
     16 ;; This program is distributed in the hope that it will be useful,
     17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
     18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     19 ;; GNU General Public License for more details.
     20 
     21 ;; You should have received a copy of the GNU General Public License
     22 ;; along with this program.  If not, see <https://www.gnu.org/licenses/>.
     23 
     24 ;;; Commentary:
     25 
     26 ;;
     27 
     28 ;;; Code:
     29 
     30 ;;; Customization
     31 
     32 (defgroup lilypond-auto-insert nil
     33   "Comprehensive auto-insert for LilyPond"
     34   :prefix "lilypond-auto-insert-"
     35   :group 'convenience)
     36 
     37 (defcustom lilypond-auto-insert-use-midi-block t
     38   "Whether or not to insert a midi block."
     39   :group 'lilypond-auto-insert
     40   :type 'boolean)
     41 
     42 (defcustom lilypond-auto-insert-header-alist
     43   '(("title")
     44     ("composer")
     45     ("tagline" . "##f"))
     46   "A list of items to put in the \"\\header\" block.
     47 
     48 Each element is a cons of (VAR . VALUE).  The key is put on the
     49 left.
     50 
     51 If the VALUE is:
     52 1. nil:  Prompt for the value, and use the result.
     53 2. A string:  Use the string.
     54 3. A sexp:  Evaluate the sexp, and use the result.
     55 
     56 NOTE: unless nil, the value (cdr) isn't stringified in the
     57 output.  This is so you can use markup or scheme syntax."
     58   :group 'lilypond-auto-insert
     59   :type '(alist :key-type string
     60 		:value-type (choice (const :tag "Prompt" nil)
     61 				    (string :tag "String")
     62 				    (sexp :tag "ELisp"))))
     63 
     64 (defcustom lilypond-auto-insert-relative t
     65   "Whether or not to use relative syntax."
     66   :group 'lilypond-auto-insert
     67   :type 'boolean)
     68 
     69 (defcustom lilypond-auto-insert-prompt-for-global nil
     70   "Whether or not to prompt for the \"\\global\" section.
     71 
     72 If non-nil, you will be prompted for:
     73 - key
     74 - time signature
     75 - tempo"
     76   :group 'lilypond-auto-insert
     77   :type 'boolean)
     78 
     79 (defcustom lilypond-auto-insert-lilypond-version "2.20"
     80   "Version of the lilypond binary.
     81 
     82 Used in the \"\\version\" statement, so choose the version you use
     83 to compile lilypond documents."
     84   :group 'lilypond-auto-insert
     85   :type 'string)
     86 
     87 (defcustom lilypond-auto-insert-language nil
     88   "Language used for `lilypond-auto-insert'.
     89 
     90 If nil, no \"\\language\" statement will be added.
     91 
     92 See \"(lilypond-notation) Note names in other languages\" for the
     93 effects of this setting."
     94   :group 'lilypond-auto-insert
     95   :type 'string)
     96 
     97 
     98 ;;; Common insertions
     99 
    100 (defun lilypond-auto-insert-boilerplate ()
    101   "Insert boilerplate lilypond code.
    102 
    103 This inserts \"\\version\" and \"\\language\" statements.
    104 
    105 See `lilypond-auto-insert-lilypond-version' and
    106 `lilypond-auto-insert-language'."
    107 
    108   (mapconcat #'identity
    109 	     (list
    110 	      (format "\\version \"%s\"\n" lilypond-auto-insert-lilypond-version)
    111 	      (when lilypond-auto-insert-language
    112 		(format "\\language \"%s\"\n" lilypond-auto-insert-language)))
    113 	     ""))
    114 
    115 (defun lilypond-auto-insert-parse-header-item (item)
    116   "Parse ITEM to be used in the \"\\header\" block."
    117   (let ((key (car item))
    118 	(value (cdr item))
    119 	(str "  %s = %s\n"))
    120     (cond ((stringp value)
    121 	   (format str key value))
    122 	  ((null value)
    123 	   (let ((answer
    124 		  (read-string
    125 		   (concat (capitalize key) ": "))))
    126 	     (unless (string-empty-p answer)
    127 	       (format str key
    128 		       (format "\"%s\"" answer)))))
    129 	  ((listp value)
    130 	   (format str key (eval value))))))
    131 
    132 (defun lilypond-auto-insert-header ()
    133   "Insert a lilypond header block.
    134 
    135 The variable `lilypond-auto-insert-header-alist' determines the
    136 contents of the header.  See that variable's documentation for
    137 details."
    138   (format "\\header {\n%s}\n"
    139 	  (mapconcat #'lilypond-auto-insert-parse-header-item
    140 		     lilypond-auto-insert-header-alist
    141 		     "")))
    142 
    143 (defun lilypond-auto-insert-global ()
    144   "Define a \"global\" section.
    145 
    146 By default, this will be empty.  Set the variable
    147 `lilypond-auto-insert-prompt-for-global' to add some
    148 interactivity."
    149   (format "global = {\n%s\n}\n"
    150 	  (if lilypond-auto-insert-prompt-for-global
    151 	      (mapconcat #'identity
    152 			 (list (format "  \\key %s"
    153 				       (read-string "Key: "))
    154 			       (format "  \\time %s"
    155 				       (read-string "Time signature: "))
    156 			       (format "  \\tempo %s"
    157 				       (read-string "Tempo: (might need quotes) ")))
    158 			 "\n")
    159 	    "")))
    160 
    161 
    162 ;;; Instruments
    163 
    164 (defun lilypond-auto-insert-instruments (&rest instruments)
    165   "Insert a definition for INSTRUMENTS.
    166 
    167 If `lilypond-auto-insert-relative' is non-nil, this will also
    168 make the blocks relative.
    169 
    170 \(lilypond-auto-insert-instruments \"violin\" \"cello\") will
    171 return a string like the following:
    172 
    173   violin = \\relative {
    174     \\global
    175   }
    176 
    177   cello = \\relative {
    178     \\global
    179   }
    180 
    181 NOTE: \"global\" is also defined when `lilypond-auto-insert' is
    182 used properly."
    183   (mapconcat
    184    (lambda (i)
    185      (format "%s = %s{\n  \\global\n}\n" i
    186 	     (when lilypond-auto-insert-relative
    187 	       "\\relative ")))
    188    instruments
    189    "\n"))
    190 
    191 
    192 ;;; Staves and stuff
    193 
    194 (defun lilypond-auto-insert-score (&optional staves)
    195   "Insert a \"\\score\" block.
    196 
    197 Optional argument STAVES is a string (usually automatically
    198 generated) containing the staves to insert within the \"\\score\"
    199 block."
    200   (let ((content (concat staves
    201 			 (when lilypond-auto-insert-use-midi-block
    202 			   "  \\midi { }\n")
    203 			 "  \\layout { }\n")))
    204     (format "\\score {\n%s}\n" content)))
    205 
    206 (defun lilypond-auto-insert-staffgroup (&rest instruments)
    207   "Insert a staff group.
    208 
    209 INSTRUMENTS is a list of instruments that make up the staff
    210 group.
    211 
    212 \(lilypond-auto-insert-staffgroup \"violin\" \"cello\") will
    213 return a string like the following:
    214 
    215 \\new StaffGroup <<
    216   \\violin
    217   \\cello
    218 >>"
    219   (format "  \\new StaffGroup <<\n%s\n  >>\n"
    220 	  (mapconcat (lambda (i)
    221 		       (format "    \\%s" i))
    222 		     instruments
    223 		     "\n")))
    224 
    225 (defun lilypond-auto-insert-choir-staff (&rest voices)
    226   "Insert a choir staff.
    227 
    228 VOICES is a list of instruments that make up the staff
    229 group.
    230 
    231 \(lilypond-auto-insert-coir-staff \"soprano\" \"alto\" \"tenor\" \"bass\") will
    232 return a string like the following:
    233 
    234 \\new ChoirStaff <<
    235   \\soprano
    236   \\alto
    237   \\tenor
    238   \\bass
    239 >>"
    240   (format "  \\new ChoirStaff <<\n%s\n  >>\n"
    241 	  (mapconcat (lambda (v)
    242 		       (format "    \\%s" v))
    243 		     voices
    244 		     "\n")))
    245 
    246 (defun lilypond-auto-insert-plain-staff (instrument)
    247   "Insert a plain staff for a single INSTRUMENT.
    248 
    249 \(lilypond-auto-insert-plain-staff \"oboe\") will return a string
    250 like the following:
    251 
    252 \\new Staff {
    253   \\oboe
    254 }"
    255   (format "  \\new Staff {\n    \\%s\n  }\n" instrument))
    256 
    257 (defun lilypond-auto-insert-plain-staves (&rest instruments)
    258   "Insert plain staves for INSTRUMENTS.
    259 
    260 \(lilypond-auto-insert-plain-staves \"horn\" \"trumpet\") will
    261 return a string like the following:
    262 
    263 \\new Staff {
    264   \\horn
    265 }
    266 
    267 \\new Staff {
    268   \\trumpet
    269 }"
    270   (mapconcat
    271    #'lilypond-auto-insert-plain-staff
    272    instruments "\n"))
    273 
    274 (defun lilypond-auto-insert-simultaneous-plain-staves (&rest instruments)
    275   "Insert simultaneous plain staves for INSTRUMENTS.
    276 
    277 Like `lilypond-auto-insert-plain-staves', but surrounds with
    278 double angle brackets.
    279 \(lilypond-auto-insert-simultaneous-plain-staves \"horn\" \"trumpet\")
    280 will return a string like the following:
    281 
    282 <<
    283   \\new Staff {
    284     \\horn
    285   }
    286 
    287   \\new Staff {
    288     \\trumpet
    289   }
    290 >>"
    291   (format "<<\n%s>>"
    292 	  (replace-regexp-in-string "^\\(.*\\)" "  \\1"
    293 				    (apply #'lilypond-auto-insert-plain-staves instruments))))
    294 
    295 (defun lilypond-auto-insert-piano-staff ()
    296   "Insert a piano staff.
    297 
    298 Always the same, but I decided to use a function for
    299 consistency's sake.  The staff is split into two parts: top and
    300 bottom."
    301   "  \\new PianoStaff <<
    302     \\new Staff \\top
    303     \\new Staff \\bottom
    304   >>\n")
    305 
    306 (defun lilypond-auto-insert-piano-and-solo-staff (instrument)
    307   "Insert a piano staff and an INSTRUMENT staff.
    308 
    309 \(lilypond-auto-insert-piano-and-solo-staff \"tin_whistle\") will
    310 return a string like the following:
    311 
    312 <<
    313   \\new Staff {
    314      \\tin_whistle
    315   }
    316   \\new PianoStaff <<
    317     \\new Staff \\top
    318     \\new Staff \\bottom
    319   >>
    320 >>"
    321   (format "  <<\n%s\n%s>>\n"
    322 	  (lilypond-auto-insert-plain-staff instrument)
    323 	  (lilypond-auto-insert-piano-staff)))
    324 
    325 (defun lilypond-auto-insert-piano-and-etc-staff (&rest instruments)
    326   "Create a staff for piano and for INSTRUMENTS.
    327 
    328 \(lilypond-auto-insert-piano-and-etc-staff \"violin\" \"cello\")
    329  will return a string like the following:
    330 
    331 <<
    332   \\new Staff {
    333     \\violin
    334   }
    335   \\new Staff {
    336     \\cello
    337   }
    338   \\new PianoStaff <<
    339     \\new Staff \\top
    340     \\new Staff \\bottom
    341   >>
    342 >>"
    343   (format "  <<\n%s\n%s>>\n"
    344 	  (apply #'lilypond-auto-insert-plain-staves instruments)
    345 	  (lilypond-auto-insert-piano-staff)))
    346 
    347 
    348 ;;; Creating the final product
    349 
    350 ;;;###autoload
    351 (defun lilypond-auto-insert-piano ()
    352   "Create a blank document for solo piano.
    353 
    354 This will prompt for:
    355 - title
    356 - composer"
    357   (interactive)
    358   (mapconcat #'identity
    359 	     (list (lilypond-auto-insert-boilerplate)
    360 		   (lilypond-auto-insert-header)
    361 		   (lilypond-auto-insert-global)
    362 		   (lilypond-auto-insert-instruments "top" "bottom")
    363 		   (lilypond-auto-insert-score
    364 		    (lilypond-auto-insert-piano-staff)))
    365 	     "\n"))
    366 
    367 ;;;###autoload
    368 (defun lilypond-auto-insert-solo ()
    369   "Create a blank document for solo instrument.
    370 
    371 This will prompt for:
    372 - title
    373 - composer
    374 - instrument"
    375   (interactive)
    376   (let ((instrument))
    377     (mapconcat #'identity
    378 	       (list (lilypond-auto-insert-boilerplate)
    379 		     (lilypond-auto-insert-header)
    380 		     (lilypond-auto-insert-global)
    381 		     (progn
    382 		       (setq instrument (read-string "Solo Instrument: "))
    383 		       (while (string-empty-p instrument)
    384 			 (setq instrument (read-string "Solo Instrument (cannot be empty): ")))
    385 		       (lilypond-auto-insert-instruments instrument))
    386 		     (lilypond-auto-insert-score
    387 		      (lilypond-auto-insert-plain-staff instrument)))
    388 	       "\n")))
    389 
    390 ;;;###autoload
    391 (defun lilypond-auto-insert-piano-and-solo ()
    392   "Create a blank document for piano and another instrument.
    393 
    394 This will prompt for:
    395 - title
    396 - composer
    397 - instrument"
    398   (interactive)
    399   (let ((instrument))
    400     (mapconcat #'identity
    401 	       (list (lilypond-auto-insert-boilerplate)
    402 		     (lilypond-auto-insert-header)
    403 		     (lilypond-auto-insert-global)
    404 		     (progn
    405 		       (setq instrument (read-string "Solo Instrument: "))
    406 		       (while (string-empty-p instrument)
    407 			 (setq instrument (read-string "Solo Instrument (cannot be empty): ")))
    408 		       (lilypond-auto-insert-instruments instrument "top" "bottom"))
    409 		     (lilypond-auto-insert-score
    410 		      (lilypond-auto-insert-piano-and-solo-staff instrument)))
    411 	       "\n")))
    412 
    413 ;;;###autoload
    414 (defun lilypond-auto-insert-satb ()
    415   "Create a blank document for SATB.
    416 
    417 This wil prompt for:
    418 - title
    419 - composer"
    420   (interactive)
    421   (mapconcat #'identity
    422 	     (list (lilypond-auto-insert-boilerplate)
    423 		   (lilypond-auto-insert-header)
    424 		   (lilypond-auto-insert-global)
    425 		   (lilypond-auto-insert-instruments "soprano" "alto" "tenor" "bass")
    426 		   (lilypond-auto-insert-score
    427 		    (lilypond-auto-insert-choir-staff "soprano" "alto" "tenor" "bass")))
    428 	     "\n"))
    429 
    430 ;;;###autoload
    431 (defun lilypond-auto-insert-string-quartet ()
    432   "Create a blank document for string quartet.
    433 
    434 This wil prompt for:
    435 - title
    436 - composer"
    437   (interactive)
    438   (mapconcat #'identity
    439 	     (list (lilypond-auto-insert-boilerplate)
    440 		   (lilypond-auto-insert-header)
    441 		   (lilypond-auto-insert-global)
    442 		   (lilypond-auto-insert-instruments "voilin_one" "voilin_two" "viola" "cello")
    443 		   (lilypond-auto-insert-score
    444 		    (lilypond-auto-insert-staffgroup "voilin_one" "voilin_two" "viola" "cello")))
    445 	     "\n"))
    446 
    447 ;;;###autoload
    448 (defun lilypond-auto-insert-etc ()
    449   "Create a blank document for an unspecified group.
    450 
    451 This wil prompt for:
    452 - title
    453 - composer
    454 - as many instruments as you want - RET on empty input will end the list."
    455   (interactive)
    456   (let ((instruments))
    457     (mapconcat #'identity
    458 	       (list (lilypond-auto-insert-boilerplate)
    459 		     (lilypond-auto-insert-header)
    460 		     (lilypond-auto-insert-global)
    461 		     (apply #'lilypond-auto-insert-instruments
    462 			    (let ((instrument (read-string "Instrument: ")))
    463 			      (while (not (string-empty-p instrument))
    464 				(push instrument instruments)
    465 				(setq instrument (read-string "Instrument: ")))
    466 			      (setq instruments (nreverse instruments))))
    467 		     (lilypond-auto-insert-score
    468 		      (apply #'lilypond-auto-insert-simultaneous-plain-staves
    469 			     instruments)))
    470 	       "\n")))
    471 
    472 ;;;###autoload
    473 (defun lilypond-auto-insert-piano-etc ()
    474   "Create a blank document for an unspecified group with piano.
    475 
    476 This wil prompt for:
    477 - title
    478 - composer
    479 - as many instruments as you want - RET on empty input will end the list."
    480   (let ((instruments))
    481     (mapconcat #'identity
    482 	       (list (lilypond-auto-insert-boilerplate)
    483 		     (lilypond-auto-insert-header)
    484 		     (lilypond-auto-insert-global)
    485 		     (apply #'lilypond-auto-insert-instruments
    486 			    (let ((instrument (read-string "Instrument: ")))
    487 			      (while (not (string-empty-p instrument))
    488 				(push instrument instruments)
    489 				(setq instrument (read-string "Instrument: ")))
    490 			      (setq instruments (nreverse instruments))))
    491 		     (lilypond-auto-insert-instruments "top" "bottom")
    492 		     (lilypond-auto-insert-score
    493 		      (apply #'lilypond-auto-insert-piano-and-etc-staff
    494 			     instruments)))
    495 	       "\n")))
    496 
    497 (defun lilypond-auto-insert-four-part ()
    498   "Create a condensed four-part harmony.
    499 
    500 A piano staff with soprano and alto in the right hand, and tenor
    501 and bass in the left hand.
    502 
    503 The tenor and bass staff will use the bass clef.
    504 
    505 This will prompt for:
    506 - title
    507 - composer"
    508   (mapconcat #'identity
    509 	     (list (lilypond-auto-insert-boilerplate)
    510 		   (lilypond-auto-insert-header)
    511 		   (lilypond-auto-insert-global)
    512 		   (lilypond-auto-insert-instruments
    513 		    "soprano" "alto" "tenor" "bass")
    514 		   (lilypond-auto-insert-score
    515 		    "  \\new PianoStaff <<
    516     \\new Staff <<
    517       \\soprano
    518       \\\\
    519       \\alto
    520     >>
    521     \\new Staff \\with {
    522       \\clef \"bass\"
    523     }
    524     <<
    525       \\tenor
    526       \\\\
    527       \\bass
    528     >>
    529   >>\n"))
    530 	     "\n"))
    531 
    532 (defvar lilypond-auto-insert-alist
    533   '((lilypond-auto-insert-solo . "Solo")
    534     (lilypond-auto-insert-piano . "Piano")
    535     (lilypond-auto-insert-piano-and-solo . "Piano and Solo Instrument")
    536     (lilypond-auto-insert-satb . "Choir or Vocal Quartet")
    537     (lilypond-auto-insert-string-quartet . "String Quartet")
    538     (lilypond-auto-insert-etc . "Group without Piano")
    539     (lilypond-auto-insert-piano-etc . "Group with Piano")
    540     (lilypond-auto-insert-four-part . "Four-part Harmony"))
    541 
    542   "Alist of instrumentations used by `lilypond-auto-insert'.
    543 
    544 To add your own:
    545     (add-to-list 'lilypond-auto-insert-alist
    546 		 '(my-lilypond-insert-function . \"Description\"))")
    547 
    548 ;;;###autoload
    549 (defun lilypond-auto-insert ()
    550   "Automatically insert lilypond in a fresh buffer.
    551 
    552 This prompts from a list of common instrumentations in
    553 `lilypond-auto-insert-alist'.  You will also be prompted for
    554 other information such as title, composer, and list of
    555 instruments if needed."
    556   (interactive)
    557   (let ((choice
    558 	 (completing-read "Instrumentation: "
    559 			  (mapcar #'cdr lilypond-auto-insert-alist))))
    560     (insert
    561      (funcall (car (rassoc choice lilypond-auto-insert-alist))))
    562     (when (equal major-mode 'LilyPond-mode)
    563       (indent-region (point-min) (point-max)))))
    564 
    565 ;;;###autoload
    566 (defun lilypond-auto-insert-on-empty-buffer ()
    567   "Call `lilypond-auto-insert' in an empty buffer.
    568 
    569 Useful if added to `LilyPond-mode-hook'."
    570   (when (= (point-min) (point-max))
    571     (lilypond-auto-insert)))
    572 
    573 (provide 'lilypond-auto-insert)
    574 ;;; lilypond-auto-insert.el ends here
    575