lilypond-auto-insert

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

lilypond-auto-insert.el (15785B)


      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://gitlab.com/jamzattack/lilypond-auto-insert
      7 ;; Keywords: abbrev, lilypond, music, typesetting, convenience
      8 ;; Version: 2020.08.21
      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 		       (lilypond-auto-insert-instruments instrument))
    384 		     (lilypond-auto-insert-score
    385 		      (lilypond-auto-insert-plain-staff instrument)))
    386 	       "\n")))
    387 
    388 ;;;###autoload
    389 (defun lilypond-auto-insert-piano-and-solo ()
    390   "Create a blank document for piano and another instrument.
    391 
    392 This will prompt for:
    393 - title
    394 - composer
    395 - instrument"
    396   (interactive)
    397   (let ((instrument))
    398     (mapconcat #'identity
    399 	       (list (lilypond-auto-insert-boilerplate)
    400 		     (lilypond-auto-insert-header)
    401 		     (lilypond-auto-insert-global)
    402 		     (progn
    403 		       (setq instrument (read-string "Solo Instrument: "))
    404 		       (lilypond-auto-insert-instruments instrument "top" "bottom"))
    405 		     (lilypond-auto-insert-score
    406 		      (lilypond-auto-insert-piano-and-solo-staff instrument)))
    407 	       "\n")))
    408 
    409 ;;;###autoload
    410 (defun lilypond-auto-insert-satb ()
    411   "Create a blank document for SATB.
    412 
    413 This wil prompt for:
    414 - title
    415 - composer"
    416   (interactive)
    417   (mapconcat #'identity
    418 	     (list (lilypond-auto-insert-boilerplate)
    419 		   (lilypond-auto-insert-header)
    420 		   (lilypond-auto-insert-global)
    421 		   (lilypond-auto-insert-instruments "soprano" "alto" "tenor" "bass")
    422 		   (lilypond-auto-insert-score
    423 		    (lilypond-auto-insert-choir-staff "soprano" "alto" "tenor" "bass")))
    424 	     "\n"))
    425 
    426 ;;;###autoload
    427 (defun lilypond-auto-insert-string-quartet ()
    428   "Create a blank document for string quartet.
    429 
    430 This wil prompt for:
    431 - title
    432 - composer"
    433   (interactive)
    434   (mapconcat #'identity
    435 	     (list (lilypond-auto-insert-boilerplate)
    436 		   (lilypond-auto-insert-header)
    437 		   (lilypond-auto-insert-global)
    438 		   (lilypond-auto-insert-instruments "voilin_one" "voilin_two" "viola" "cello")
    439 		   (lilypond-auto-insert-score
    440 		    (lilypond-auto-insert-staffgroup "voilin_one" "voilin_two" "viola" "cello")))
    441 	     "\n"))
    442 
    443 ;;;###autoload
    444 (defun lilypond-auto-insert-etc ()
    445   "Create a blank document for an unspecified group.
    446 
    447 This wil prompt for:
    448 - title
    449 - composer
    450 - as many instruments as you want - RET on empty input will end the list."
    451   (interactive)
    452   (let ((instruments))
    453     (mapconcat #'identity
    454 	       (list (lilypond-auto-insert-boilerplate)
    455 		     (lilypond-auto-insert-header)
    456 		     (lilypond-auto-insert-global)
    457 		     (apply #'lilypond-auto-insert-instruments
    458 			    (let ((instrument (read-string "Instrument: ")))
    459 			      (while (not (string-empty-p instrument))
    460 				(push instrument instruments)
    461 				(setq instrument (read-string "Instrument: ")))
    462 			      (setq instruments (nreverse instruments))))
    463 		     (lilypond-auto-insert-score
    464 		      (apply #'lilypond-auto-insert-simultaneous-plain-staves
    465 			     instruments)))
    466 	       "\n")))
    467 
    468 ;;;###autoload
    469 (defun lilypond-auto-insert-piano-etc ()
    470   "Create a blank document for an unspecified group with piano.
    471 
    472 This wil prompt for:
    473 - title
    474 - composer
    475 - as many instruments as you want - RET on empty input will end the list."
    476   (let ((instruments))
    477     (mapconcat #'identity
    478 	       (list (lilypond-auto-insert-boilerplate)
    479 		     (lilypond-auto-insert-header)
    480 		     (lilypond-auto-insert-global)
    481 		     (apply #'lilypond-auto-insert-instruments
    482 			    (let ((instrument (read-string "Instrument: ")))
    483 			      (while (not (string-empty-p instrument))
    484 				(push instrument instruments)
    485 				(setq instrument (read-string "Instrument: ")))
    486 			      (setq instruments (nreverse instruments))))
    487 		     (lilypond-auto-insert-instruments "top" "bottom")
    488 		     (lilypond-auto-insert-score
    489 		      (apply #'lilypond-auto-insert-piano-and-etc-staff
    490 			     instruments)))
    491 	       "\n")))
    492 
    493 (defun lilypond-auto-insert-four-part ()
    494   "Create a condensed four-part harmony.
    495 
    496 A piano staff with soprano and alto in the right hand, and tenor
    497 and bass in the left hand.
    498 
    499 The tenor and bass staff will use the bass clef.
    500 
    501 This will prompt for:
    502 - title
    503 - composer"
    504   (mapconcat #'identity
    505 	     (list (lilypond-auto-insert-boilerplate)
    506 		   (lilypond-auto-insert-header)
    507 		   (lilypond-auto-insert-global)
    508 		   (lilypond-auto-insert-instruments
    509 		    "soprano" "alto" "tenor" "bass")
    510 		   (lilypond-auto-insert-score
    511 		    "  \\new PianoStaff <<
    512     \\new Staff <<
    513       \\soprano
    514       \\\\
    515       \\alto
    516     >>
    517     \\new Staff \\with {
    518       \\clef \"bass\"
    519     }
    520     <<
    521       \\tenor
    522       \\\\
    523       \\bass
    524     >>
    525   >>\n"))
    526 	     "\n"))
    527 
    528 (defvar lilypond-auto-insert-alist
    529   '((lilypond-auto-insert-solo . "Solo")
    530     (lilypond-auto-insert-piano . "Piano")
    531     (lilypond-auto-insert-piano-and-solo . "Piano and Solo Instrument")
    532     (lilypond-auto-insert-satb . "Choir or Vocal Quartet")
    533     (lilypond-auto-insert-string-quartet . "String Quartet")
    534     (lilypond-auto-insert-etc . "Group without Piano")
    535     (lilypond-auto-insert-piano-etc . "Group with Piano")
    536     (lilypond-auto-insert-four-part . "Four-part Harmony"))
    537 
    538   "Alist of instrumentations used by `lilypond-auto-insert'.
    539 
    540 To add your own:
    541     (add-to-list 'lilypond-auto-insert-alist
    542 		 '(my-lilypond-insert-function . \"Description\"))")
    543 
    544 ;;;###autoload
    545 (defun lilypond-auto-insert ()
    546   "Automatically insert lilypond in a fresh buffer.
    547 
    548 This prompts from a list of common instrumentations in
    549 `lilypond-auto-insert-alist'.  You will also be prompted for
    550 other information such as title, composer, and list of
    551 instruments if needed."
    552   (interactive)
    553   (let ((choice
    554 	 (completing-read "Instrumentation: "
    555 			  (mapcar #'cdr lilypond-auto-insert-alist))))
    556     (insert
    557      (funcall (car (rassoc choice lilypond-auto-insert-alist))))
    558     (when (equal major-mode 'LilyPond-mode)
    559       (indent-region (point-min) (point-max)))))
    560 
    561 ;;;###autoload
    562 (defun lilypond-auto-insert-on-empty-buffer ()
    563   "Call `lilypond-auto-insert' in an empty buffer.
    564 
    565 Useful if added to `LilyPond-mode-hook'."
    566   (when (= (point-min) (point-max))
    567     (lilypond-auto-insert)))
    568 
    569 (provide 'lilypond-auto-insert)
    570 ;;; lilypond-auto-insert.el ends here
    571