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