\version "2.19.24" #(define-public (my-determine-frets context notes specified-info . rest) "Determine string numbers and frets for playing @var{notes} as a chord, given specified information @var{specified-info}. @var{specified-info} is a list with two list elements, specified strings @code{defined-strings} and specified fingerings @code{defined-fingers}. Only a fingering address@hidden will affect the fret selection, as it specifies an open string. If @code{defined-strings} is @code{'()}, the context property @code{defaultStrings} will be used as a list of defined strings. Will look for predefined fretboards if @code{predefinedFretboardTable} is not @code {#f}. If @var{rest} is present, it contains the @code{FretBoard} grob, and a fretboard will be created. Otherwise, a list of @code{(string fret finger)} lists will be returned." ;; helper functions (define (string-frets->placement-list string-frets string-count) "Convert @var{string-frets} to @code{fret-diagram-verbose} dot placement entries." (let* ((placements (list->vector (map (lambda (x) (list 'mute x)) (iota string-count 1))))) (for-each (lambda (sf) (let* ((string (car sf)) (fret (cadr sf)) (finger (caddr sf))) (vector-set! placements (1- string) (if (= 0 fret) (list 'open string) (if finger (list 'place-fret string fret finger) (list 'place-fret string fret)))))) string-frets) (vector->list placements))) (define (placement-list->string-frets placement-list) "Convert @var{placement-list} to string-fret list." (map (lambda (x) (if (eq? (car x) 'place-fret) (cdr x) (list (cadr x) 0))) (filter (lambda (l) (or (eq? (car l) 'place-fret) (eq? (car l) 'open))) placement-list))) (define (entry-count art-list) "Count the number of entries in a list of articulations." (length (filter (lambda (x) (not (null? x))) art-list))) (define (string-number event) "Get the string-number from @var{event}. Return @var{#f} if no string-number is present." (let ((num (ly:event-property event 'string-number))) (and (integer? num) (positive? num) num))) (define (determine-frets-and-strings notes defined-strings defined-fingers minimum-fret maximum-stretch tuning) "Determine the frets and strings used to play the notes in @var{notes}, given @var{defined-strings} and @var{defined-fingers} along with @var{minimum-fret}, @var{maximum-stretch}, and @var{tuning}. Returns a list of @code{(string fret finger) lists." (define restrain-open-strings (ly:context-property context 'restrainOpenStrings #f)) (define specified-frets '()) (define free-strings (iota (length tuning) 1)) (define (calc-fret pitch string tuning) "Calculate the fret to play @var{pitch} on @var{string} with @var{tuning}." (* 2 (- (ly:pitch-tones pitch) (ly:pitch-tones (list-ref tuning (1- string)))))) (define (note-pitch note) "Get the pitch (in semitones) from @var{note}." (ly:event-property note 'pitch)) (define (note-finger ev) "Get the fingering from @var{ev}. Return @var{#f} if no fingering is present." (let* ((articulations (ly:event-property ev 'articulations)) (finger-found #f)) (for-each (lambda (art) (let* ((num (ly:event-property art 'digit))) (if (and (ly:in-event-class? art 'fingering-event) (number? num) (> num 0)) (set! finger-found num)))) articulations) finger-found)) (define (delete-free-string string) (if (number? string) (set! free-strings (delete string free-strings)))) (define (close-enough fret) "Decide if @var{fret} is acceptable, given the already used frets." (every (lambda (specced-fret) (or (zero? specced-fret) (zero? fret) (>= maximum-stretch (abs (- fret specced-fret))))) specified-frets)) (define (string-qualifies string pitch) "Can @var{pitch} be played on @var{string}, given already placed notes?" (let* ((fret (calc-fret pitch string tuning))) (and (or (and (not restrain-open-strings) (zero? fret)) (>= fret minimum-fret)) (integer? (truncate fret)) (close-enough fret)))) (define (open-string string pitch) "Is @var{pitch} and open-string note on @var{string}, given the current tuning?" (let* ((fret (calc-fret pitch string tuning))) (zero? fret))) (define (set-fret! pitch-entry string finger) (let* ((this-fret (calc-fret (car pitch-entry) string tuning))) (if (< this-fret 0) (ly:warning (_ "Negative fret for pitch ~a on string ~a") (car pitch-entry) string) (if (not (integer? (truncate this-fret))) (ly:warning (_ "Missing fret for pitch ~a on string ~a") (car pitch-entry) string))) (delete-free-string string) (set! specified-frets (cons this-fret specified-frets)) (list-set! string-fret-fingers (cdr pitch-entry) (list string this-fret finger)))) (define (kill-note! string-fret-fingers note-index) (list-set! string-fret-fingers note-index (list #f #t))) (define string-fret-fingers (map (lambda (string finger) (if (null? finger) (list string #f) (list string #f finger))) defined-strings defined-fingers)) ;;; body of determine-frets-and-strings (let* ((pitches (map note-pitch notes)) (pitch-alist (map cons pitches (iota (length pitches))))) ;; handle notes with strings assigned and fingering of 0 (for-each (lambda (pitch-entry string-fret-finger) (let* ((string (list-ref string-fret-finger 0)) (finger (if (= (length string-fret-finger) 3) (list-ref string-fret-finger 2) '())) (pitch (car pitch-entry)) (digit (if (null? finger) #f finger))) (if (or (not (null? string)) (eqv? digit 0)) (if (eqv? digit 0) ;; here we handle fingers of 0 -- open strings (let ((fit-string (find (lambda (string) (open-string string pitch)) free-strings))) (if fit-string (set-fret! pitch-entry fit-string #f) (ly:warning (_ "No open string for pitch ~a") pitch))) ;; here we handle assigned strings (let ((this-fret (calc-fret pitch string tuning)) (handle-negative (ly:context-property context 'handleNegativeFrets 'recalculate))) (cond ((or (and (>= this-fret 0) (integer? this-fret)) (eq? handle-negative 'include)) (set-fret! pitch-entry string finger)) ((eq? handle-negative 'recalculate) (begin (ly:warning (_ "Requested string for pitch requires negative fret: string ~a pitch ~a") string pitch) (ly:warning (_ "Ignoring string request and recalculating.")) (list-set! string-fret-fingers (cdr pitch-entry) (if (null? finger) (list '() #f) (list '() #f finger))))) ((eq? handle-negative 'ignore) (begin (ly:warning (_ "Requested string for pitch requires negative fret: string ~a pitch ~a") string pitch) (ly:warning (_ "Ignoring note in tablature.")) (kill-note! string-fret-fingers (cdr pitch-entry)))))))))) pitch-alist string-fret-fingers) ;; handle notes without strings assigned -- sorted by pitch, so ;; we need to use the alist to have the note number available (for-each (lambda (pitch-entry) (let* ((string-fret-finger (list-ref string-fret-fingers (cdr pitch-entry))) (string (list-ref string-fret-finger 0)) (finger (if (= (length string-fret-finger) 3) (list-ref string-fret-finger 2) '())) (pitch (car pitch-entry)) (fit-string (find (lambda (string) (string-qualifies string pitch)) free-strings))) (if (not (list-ref string-fret-finger 1)) (if fit-string (set-fret! pitch-entry fit-string finger) (begin (ly:event-warning (list-ref notes (cdr pitch-entry)) (_ "No string for pitch ~a (given frets ~a)") pitch specified-frets) (kill-note! string-fret-fingers (cdr pitch-entry))))))) (sort pitch-alist (lambda (pitch-entry-a pitch-entry-b) (ly:pitchplacement-list (filter (lambda (entry) (car entry)) string-frets) string-count)))) (if (null? grob) (placement-list->string-frets predefined-fretboard) (create-fretboard context grob predefined-fretboard))))) #(define (integer-and-fraction nmbr) "Return a list with an integer and a fraction build from an exact number Example: 47/7 -> (6 5/7) " (let* ((i (inexact->exact (truncate nmbr))) (rest (string->number (format #f "~a~a" "#e" (- nmbr i))))) ;; return a string ;(format #f "~a~a" ; i ; (if (= rest 0) ; "" ; (string-append " " (number->string rest)))) ;; return a list (list i rest) )) %% Better to do formatting not in `my-determine-frets' #(define my-format-tab-note-head (lambda (grob) (let* ((txt (ly:grob-property grob 'text)) (nmbr (if (not (null? txt)) (car (last-pair txt)) '()))) (if (string? nmbr) (let* ((val (integer-and-fraction (string->number nmbr))) (fret (if (and (zero? (car val)) (not (zero? (cadr val)))) "" (number->string (car val)))) (frac (cond ((zero? (cadr val)) "") ((string-null? fret) (number->string (cadr val))) (else (markup #:fontsize -2.5 (number->string (cadr val))))))) (ly:grob-set-property! grob 'text #{ \markup \concat \vcenter { #fret #frac } #})) txt)))) \layout { \context { \Score noteToFretFunction = #my-determine-frets } \context { \TabStaff \override TabNoteHead.before-line-breaking = #my-format-tab-note-head } } %%%% Only compile with one of the below examples. Don't uncomment two of them %%%% \language "english", "makam.ly", \include "arabic.ly" may disturb %%%% each other %%%%%%%%%%%%%%%%%%%%%%%% %% ENGLISH NOTE NAMES %%%%%%%%%%%%%%%%%%%%%%%% %%{ \language "english" m = \relative { \cadenzaOn <>^"E" e, eqs es etqs ess <>^"F" \bar "||" ff fqf f fqs fs ftqs fss <>^"G" \bar "||" gff gtqf gf gqf g gqs gs gtqs gss <>^"A" \bar "||" aff atqf af aqf a aqs as atqs ass <>^"B" \bar "||" bff btqf bf bqf b bqs bs btqs bss <>^"C" \bar "||" cff ctqf cf cqf c cqs cs ctqs css <>^"D" \bar "||" dff dtqf df dqf d dqs ds dtqs dss <>^"E" \bar "||" eff etqf ef eqf e eqs es etqs ess <>^"F" \bar "||" fff ftqf ff fqf f fqs fs ftqs fss <>^"G" \bar "||" gff gtqf gf gqf g gqs gs gtqs gss <>^"A" \bar "||" aff atqf af aqf a aqs as atqs ass <>^"B" \bar "||" bff btqf bf bqf b bqs bs btqs bss <>^"C" \bar "||" cff ctqf cf cqf c cqs cs ctqs css <>^"D" \bar "||" dff dtqf df dqf d dqs ds dtqs dss <>^"E" \bar "||" eff etqf ef eqf e eqs es etqs ess <>^"F" \bar "||" fff ftqf ff fqf f fqs fs ftqs fss <>^"G" \bar "||" gff gtqf gf gqf g gqs gs gtqs gss <>^"A" \bar "||" aff atqf af aqf a aqs as atqs ass \bar "||" <>^"B" \bar "||" bff btqf bf bqf b bqs bs btqs bss <>^"C" \bar "||" cff ctqf cf cqf c cqs cs ctqs css <>^"D" \bar "||" dff dtqf df dqf d dqs ds dtqs dss \bar "||" } %} %%%%%%%%%%%%%%%%%%%%%%%% %% "makam.ly" %%%%%%%%%%%%%%%%%%%%%%%% %{ \include "makam.ly" m = \relative { \cadenzaOn c d e f g a b \bar "||" cc, dc ec fc gc ac bc \bar "||" cb, db eb fb gb ab bb \bar "||" ck, dk ek fk gk ak bk \bar "||" cbm, dbm ebm fbm gbm abm bbm \bar "||" cfc, dfc efc ffc gfc afc bfc \bar "||" cfb, dfb efb ffb gfb afb bfb \bar "||" cfk, dfk efk ffk gfk afk bfk \bar "||" cfi, dfi efi ffi gfi afi bfi \bar "||" cfu, dfu efu ffu gfu afu bfu \bar "||" cfbm, dfbm efbm ffbm gfbm afbm bfbm \bar "||" } %} %%%%%%%%%%%%%%%%%%%%%%%% %% "arabic.ly" %%%%%%%%%%%%%%%%%%%%%%%% %{ \include "arabic.ly" m = \relative { \cadenzaOn dobb dobsb dob dosb do dosd dod dodsd dodd \bar "||" rebb rebsb reb resb re resd red redsd redd \bar "||" mibb mibsb mib misb mi misd mid midsd midd \bar "||" fabb fabsb fab fasb fa fasd fad fadsd fadd \bar "||" solbb solbsb solb solsb sol solsd sold soldsd soldd \bar "||" labb labsb lab lasb la lasd lad ladsd ladd \bar "||" sibb sibsb sib sisb si sisd sid sidsd sidd \bar "||" } %} << \new Staff << \clef "G_8" \m >> \new TabStaff << \m >> >>