]> git.donarmstrong.com Git - lilypond.git/commitdiff
Make tab-note-heads and fretboards use common code.
authorCarl Sorensen <c_sorensen@byu.edu>
Sat, 16 Jan 2010 01:15:35 +0000 (18:15 -0700)
committerCarl Sorensen <c_sorensen@byu.edu>
Sat, 30 Jan 2010 23:35:09 +0000 (16:35 -0700)
This combines the string and fret assigning code of the
tab-note-heads engraver and the fret-boards engraver.  Both
will use scheme procedures defined in scm/translation-functions.scm
and specified by context properties initialized in ly/engraver-init.ly

 * Modify the calling sequence of noteToFretFunction so that it
   takes a context, a note-list, a string-list, and an optional
   grob.  If grob is included, noteToFretFunction will add the
   fretboard to the grob.  If grob is not included, noteToFretFunction
   will return a list of (string fret finger) tuples.

 * Refactor the code in scm/translation-functions.scm to support
   this separation of function.

 * predefinedDiagramTable is now a context property for TabStaff as
   well as for FretBoards.  This means that if a chord is present in
   TabStaff, and predefinedDiagramTable for that TabStaff is not #f,
   and a predefined diagram for the chord exists, the TabStaff will
   display the notes of the predefined diagram.

 * Change tab-note-heads-engraver so that it calls noteToFretFunction
   instead of having the fret calculation code inside the engraver.

 * Change the noteToFretFunction calling sequence in fretboard-engraver
   to match the new definition

 * Add an optional default argument to ly:context-property so we
   can set a default at call time

lily/context-scheme.cc
lily/fretboard-engraver.cc
lily/note-heads-engraver.cc
lily/tab-note-heads-engraver.cc
ly/engraver-init.ly
ly/property-init.ly
scm/define-context-properties.scm
scm/translation-functions.scm

index bb55e1bc75eb847596ada3ef9eb2118e9332637c..8f43236d86c794dc29fbf6b385d581c1d35f405e 100644 (file)
@@ -66,7 +66,7 @@ LY_DEFINE (ly_context_grob_definition, "ly:context-grob-definition",
           " @var{context} as an alist.")
 {
   Context *tr = unsmob_context (context);
-  
+
   LY_ASSERT_SMOB (Context, context, 1);
   LY_ASSERT_TYPE (ly_is_symbol, name, 2);
 
@@ -92,14 +92,17 @@ LY_DEFINE (ly_context_pushpop_property, "ly:context-pushpop-property",
 }
 
 LY_DEFINE (ly_context_property, "ly:context-property",
-          2, 0, 0, (SCM context, SCM sym),
-          "Return the value for property @var{sym} in @var{context}.")
+           2, 1, 0, (SCM context, SCM sym, SCM def),
+           "Return the value for property @var{sym} in @var{context}."
+           " If @var{def} is given, and property value is @code{'()},"
+           " return @var{def}.")
 {
   LY_ASSERT_SMOB (Context, context, 1);
   LY_ASSERT_TYPE (ly_is_symbol, sym, 2);
 
   Context *t = unsmob_context (context);
-  return t->internal_get_property (sym);
+  SCM result = t->internal_get_property (sym);
+  return def != SCM_UNDEFINED && scm_is_null (result) ? def : result;
 }
 
 LY_DEFINE (ly_context_set_property_x, "ly:context-set-property!",
@@ -124,7 +127,7 @@ LY_DEFINE (ly_context_property_where_defined, "ly:context-property-where-defined
 {
   LY_ASSERT_SMOB (Context, context, 1);
   LY_ASSERT_TYPE (ly_is_symbol, name, 2);
-  
+
   Context *tr = unsmob_context (context);
 
   SCM val;
@@ -142,7 +145,7 @@ LY_DEFINE (ly_context_unset_property, "ly:context-unset-property", 2, 0, 0,
   LY_ASSERT_SMOB (Context, context, 1);
   LY_ASSERT_TYPE (ly_is_symbol, name, 2);
   Context *tr = unsmob_context (context);
-  
+
   tr->unset_property (name);
   return SCM_UNSPECIFIED;
 }
index e1b78a52206c1e08ef777d2c71b4f1248c569c2d..98375a3db77471a2d75c331751548d84eb0c3ec4 100644 (file)
@@ -36,7 +36,7 @@ using namespace std;
 class Fretboard_engraver : public Engraver
 {
   Item *fret_board_;
-  
+
   vector<Stream_event*> note_events_;
   vector<Stream_event*> tabstring_events_;
 public:
@@ -90,18 +90,16 @@ Fretboard_engraver::process_music ()
   SCM fret_notes = ly_cxx_vector_to_list (note_events_);
   SCM proc = get_property ("noteToFretFunction");
   if (ly_is_procedure (proc))
-    {
      scm_call_4 (proc,
-                context ()->self_scm (),
-                fret_board_->self_scm (),
-                fret_notes,           
-                ly_cxx_vector_to_list (tabstring_events_));
-    }
-  SCM changes = get_property("chordChanges");
-  if (to_boolean (changes) && scm_is_pair(last_fret_notes_)
+                 context ()->self_scm (),
+                 fret_notes,
+                 ly_cxx_vector_to_list (tabstring_events_),
+                 fret_board_->self_scm ());
+  SCM changes = get_property ("chordChanges");
+  if (to_boolean (changes) && scm_is_pair (last_fret_notes_)
       && ly_is_equal (last_fret_notes_, fret_notes))
     fret_board_->set_property ("begin-of-line-visible", SCM_BOOL_T);
-  
+
   last_fret_notes_ = fret_notes;
 }
 
@@ -115,7 +113,7 @@ Fretboard_engraver::stop_translation_timestep ()
 
 ADD_TRANSLATOR (Fretboard_engraver,
                /* doc */
-               "Generate one or more tablature noteheads from event of type"
+                "Generate fret diagram from one or more events of type"
                " @code{NoteEvent}.",
 
                /* create */
@@ -123,12 +121,13 @@ ADD_TRANSLATOR (Fretboard_engraver,
 
                /* read */
                 "chordChanges "
-               "stringTunings "
-               "minimumFret "
-                "maximumFretStretch "
-               "tablatureFormat "
                "highStringOne "
-                "predefinedDiagramTable",
+                "maximumFretStretch "
+               "minimumFret "
+                "noteToFretFunction "
+                "predefinedDiagramTable "
+               "stringTunings "
+                "tablatureFormat ",
 
                /* write */
                ""
index a549d0a69ad2599389b00ee5a1964e8802302919..807e8cc121eeed752f438e0d4e04bf85f5cccb80 100644 (file)
@@ -63,7 +63,7 @@ Note_heads_engraver::process_music ()
 {
   SCM c0 = get_property ("middleCPosition");
   SCM layout_proc = get_property("staffLineLayoutFunction");
-      
+
   for (vsize i = 0; i < note_evs_.size (); i++)
     {
       Stream_event *ev = note_evs_[i];
@@ -84,12 +84,12 @@ Note_heads_engraver::process_music ()
        SCM pitch = ev->get_property("pitch");
        pos = scm_to_int(scm_call_1 (layout_proc, pitch));
       }
-      else 
+      else
        pos = pit->steps ();
 
       if (scm_is_number (c0))
        pos += scm_to_int(c0);
-      
+
       note->set_property ("staff-position", scm_from_int (pos));
 
       /*
index 5dd0c3d8fc8d75a2e190df5b0829561880945ac2..e9c99d12a8860f8d0c4821b5f266a9964ec0460f 100644 (file)
@@ -77,16 +77,21 @@ void
 Tab_note_heads_engraver::process_music ()
 {
   vsize j = 0;
+
+  vector<Stream_event *> string_events;
+
   for (vsize i = 0; i < note_events_.size (); i++)
     {
-      SCM string_tunings = get_property ("stringTunings");
-      int string_count = scm_ilength (string_tunings);
-      bool high_string_one = to_boolean (get_property ("highStringOne"));
 
       Stream_event *event = note_events_[i];
 
       Stream_event *tabstring_event = 0;
 
+      /*
+         For notes inside a chord construct, string indications are
+         stored as articulations on the note, so we check through
+         the notes
+      */
       for (SCM s = event->get_property ("articulations");
           !tabstring_event && scm_is_pair (s); s = scm_cdr (s))
        {
@@ -96,62 +101,65 @@ Tab_note_heads_engraver::process_music ()
            tabstring_event = art;
        }
 
+      /*
+         For string indications listed outside a chord construct,
+         a string_number_event is generated, so if there was no string
+         in the articulations, we check for string events outside
+         the chord construct
+      */
       if (!tabstring_event && j < tabstring_events_.size ())
        {
          tabstring_event = tabstring_events_[j];
          if (j + 1 < tabstring_events_.size ())
            j++;
        }
-
-      int string_number = 0;
       if (tabstring_event)
-       string_number = scm_to_int (tabstring_event->get_property ("string-number"));
-
-      if (!string_number)
-       {
-         SCM scm_pitch = event->get_property ("pitch");
-         int min_fret = robust_scm2int (get_property ("minimumFret"), 0);
-         int start = (high_string_one) ? 1 : string_count;
-         int end = (high_string_one) ? string_count+1 : 0;
-
-         int i = start;
-         do
-           {
-             int fret = unsmob_pitch (scm_pitch)->rounded_semitone_pitch ()
-               - scm_to_int (robust_list_ref (i - 1, string_tunings));
-         
-             if (fret >= min_fret)
-               {
-                 string_number = i;
-                 break;
-               }
-             i += high_string_one ? 1 : -1;
-           }
-         while (i != end);
-       }
-      
-      if (string_number)
-       {
-         SCM proc = get_property ("tablatureFormat");
-         SCM text = scm_call_3 (proc, scm_from_int (string_number),
-                                context ()->self_scm (),
-                                event->self_scm ());
-         Item *note = make_item ("TabNoteHead", event->self_scm ());
-         note->set_property ("text", text);
-
-
-         int pos = 2 * string_number - string_count - 1; // No tab-note between the string !!!
-         if (to_boolean (get_property ("stringOneTopmost")))
-           pos = - pos;
-
-         note->set_property ("staff-position", scm_from_int (pos));
-      
-         notes_.push_back (note);
-       }
-      else
-       event->origin ()->warning ("could not calculate a string number.");
+        string_events.push_back (tabstring_event);
     }
+
+  SCM tab_notes = ly_cxx_vector_to_list (note_events_);
+  SCM tab_strings = SCM_EOL;
+  if (string_events.size ())
+    tab_strings = ly_cxx_vector_to_list (string_events);
+  SCM proc = get_property ("noteToFretFunction");
+  SCM string_fret_finger = SCM_EOL;
+  if (ly_is_procedure (proc))
+    string_fret_finger = scm_call_3 (proc,
+                                     context ()->self_scm (),
+                                     tab_notes,
+                                     tab_strings);
+  SCM note_entry = SCM_EOL;
+  SCM string_number = SCM_EOL;
+  SCM fret = SCM_EOL;
+  SCM fret_label = SCM_EOL;
+  SCM fret_procedure = get_property ("tablatureFormat");
+  SCM staff_line_procedure = get_property ("tabStaffLineLayoutFunction");
+  SCM staff_position = SCM_EOL;
+  vsize fret_count = (vsize) scm_ilength (string_fret_finger);
+  bool length_changed = (note_events_.size () != fret_count);
+  vsize index;
+
+  if (string_fret_finger != SCM_EOL)
+    for (vsize i=0; i < fret_count; i++)
+      {
+         note_entry = scm_list_ref (string_fret_finger, scm_from_int (i));
+         string_number = scm_car (note_entry);
+         fret = scm_cadr (note_entry);
+         fret_label = scm_call_3 (fret_procedure,
+                                  context ()->self_scm (),
+                                  string_number,
+                                  fret);
+         index = length_changed ? 0 : i;
+         Item *note = make_item ("TabNoteHead", note_events_[index]->self_scm ());
+         note->set_property ("text", fret_label);
+         staff_position = scm_call_2 (staff_line_procedure,
+                                      context ()->self_scm (),
+                                      string_number);
+         note->set_property ("staff-position", staff_position);
+         notes_.push_back (note);
+      }
 }
+
 void
 Tab_note_heads_engraver::stop_translation_timestep ()
 {
@@ -173,9 +181,11 @@ ADD_TRANSLATOR (Tab_note_heads_engraver,
                "highStringOne "
                "middleCPosition "
                "minimumFret "
+                "noteToFretFunction "
                "stringOneTopmost "
                "stringTunings "
-               "tablatureFormat ",
+                "tablatureFormat "
+                "tabStaffLineLayoutFunction ",
 
                /* write */ ""
                );
index f7a58b4d01da310c224aaa6b50e56d0f858195ba..c145db09ced0299c97b49ed8862a912ddce455cf 100644 (file)
@@ -527,6 +527,7 @@ automatically when an output definition (a @code{\score} or
 
 
   noteToFretFunction = #determine-frets
+  predefinedDiagramTable = ##f
   soloText = #"Solo"
   soloIIText = #"Solo II"
   aDueText = #"a2"
@@ -617,10 +618,11 @@ automatically when an output definition (a @code{\score} or
   stringOneTopmost = ##t
   highStringOne = ##t
 
-%% One may change the strings tuning as following :
-%% The lenght of the list must be equal to the number of string
+%% One may change the string tunings as follows :
+%% The length of the list must be equal to the number of strings
   stringTunings = #guitar-tuning
   tablatureFormat = #fret-number-tablature-format
+  tabStaffLineLayoutFunction = #tablature-position-on-lines
 
 %%
   figuredBassFormatter = #format-bass-figure
index 788cabb96c9412019e4541c71d8d98c7e39fe6dd..954dae2b5fc85b3e8a0acee7cc0656c922d1ece6 100644 (file)
@@ -320,9 +320,9 @@ back to the lilypond source statement.")
 %% predefined fretboards
 
 predefinedFretboardsOff =
-  \set FretBoards.predefinedDiagramTable = ##f
+  \set predefinedDiagramTable = ##f
 predefinedFretboardsOn =
-  \set FretBoards.predefinedDiagramTable = #fretboard-table
+  \set predefinedDiagramTable = #fretboard-table
 
 
 %% shape note heads
index f0d58babc37e4f1954f90d4747542674c3d943e7..ab5836bdbada29ca00d6402324e463f7ececc572 100644 (file)
@@ -347,8 +347,10 @@ repeated section for a page turn to be allowed within that section.")
 
      (noChordSymbol ,markup? "Markup to be displayed for rests in a
 ChordNames context.")
-     (noteToFretFunction ,procedure? "How to produce a fret diagram.
-Parameters: A list of note events and a list of tabstring events.")
+     (noteToFretFunction ,procedure? "Convert list of notes and list of
+defined strings to full list of strings and fret numbers.
+Parameters: The context, a list of note events, a list of
+tabstring events, and the fretboard grob if a fretboard is desired.")
 
 
      (ottavation ,markup? "If set, the text for an ottava spanner.
@@ -454,8 +456,11 @@ the nesting of a start delimiters.")
 
 
      (tablatureFormat ,procedure? "A function formatting a tablature
-note head.  Called with three arguments: string number, context and event.
-It returns the text as a string.")
+note head.  Called with three arguments: context, string number and,
+fret number.  It returns the text as a markup.")
+     (tabStaffLineLayoutFunction ,procedure? "A function determining the
+staff position of a tablature note head.  Called with two arguments:
+the context and the string.")
      (tempoHideNote ,boolean? "Hide the note=count in tempo marks.")
      (tempoText ,markup? "Text for tempo marks.")
      (tempoUnitCount ,number? "Count for specifying tempo.")
index 45f20b3094d21e78ce64e517b3f2fb345e5e6508..ece0031b78ab485b2672784ad9c8b9029a8a00f7 100644 (file)
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; fret diagrams
 
-(define-public (determine-frets context grob notes string-numbers)
+(define (create-fretboard context grob placement-list)
+  "Convert @var{placement-list} into a fretboard @var{grob}."
 
-  (define (ensure-number a b)
-    (if (number? a)
-       a
-       b))
+  (let* ((tunings (ly:context-property context 'stringTunings))
+        (my-string-count (length tunings))
+        (details (ly:grob-property grob 'fret-diagram-details)))
 
-  (define (string-frets->dot-placement string-frets string-count)
-    (let* ((desc (list->vector
-                 (map (lambda (x) (list 'mute  (1+ x)))
-                      (iota string-count)))))
+    ;; Add string-count from string-tunings to fret-diagram-details.
+    (set! (ly:grob-property grob 'fret-diagram-details)
+            (acons 'string-count my-string-count details))
+    ;; Create the dot-placement list for the grob
+    (set! (ly:grob-property grob 'dot-placement-list) placement-list)))
+
+(define-public
+  (determine-frets context notes defined-strings . rest)
+  "Determine string numbers and frets for playing @var{notes}
+as a chord, given specified string numbers @var{defined-strings}.
+Will look for predefined fretboards if @code{predefinedFretboardTable}
+is not @code {#f}.  If @var{rest} is present, it contains the
+FretBoard grob, and a fretboard will be
+created.  Otherwise, a list of (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  (1+ x)))
+                            (iota string-count)))))
 
       (for-each (lambda (sf)
                  (let* ((string (car sf))
                         (fret (cadr sf))
                         (finger (caddr sf)))
-
                    (vector-set!
-                    desc (1- string)
+                     placements (1- string)
                     (if (= 0 fret)
-                        (list 'open string)
+                         (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) (cdr x))
+         (filter (lambda (l) (eq? (cdr l) 'place-fret) placement-list))))
 
-      (vector->list desc)))
 
   (define (get-predefined-fretboard predefined-fret-table tuning pitches)
-;   (_i "Search through @var{predefined-fret-table} looking for a predefined
-;fretboard with a key of @var{(tuning . pitches)}.  The search will check
-;both up and down an octave in order to accomodate transposition of the
-;chords.")
+    "Search through @var{predefined-fret-table} looking for a predefined
+fretboard with a key of @var{(tuning . pitches)}.  The search will check
+both up and down an octave in order to accomodate transposition of the
+chords.  Returns a placement-list."
+
     (define (get-fretboard key)
       (let ((hash-handle
             (hash-get-handle predefined-fret-table key)))
            (cdr hash-handle)  ; return table entry
            '())))
 
+    ;; body of get-predefined-fretboard
     (let ((test-fretboard (get-fretboard (cons tuning pitches))))
       (if (not (null? test-fretboard))
          test-fretboard
                 (cons tuning (map (lambda (x) (shift-octave x -1))
                                   pitches))))))))
 
-;; body.
-  (let* ((tunings (ly:context-property context 'stringTunings))
-        (my-string-count (length tunings))
-        (details (ly:grob-property grob 'fret-diagram-details))
-        (predefined-frets
+  ;; body of determine-frets
+  (let* ((predefined-fret-table
          (ly:context-property context 'predefinedDiagramTable))
+         (tunings (ly:context-property context 'stringTunings))
+         (string-count (length tunings))
+         (grob (if (null? rest) '() (car rest)))
         (pitches (map (lambda (x) (ly:event-property x 'pitch)) notes))
          (predefined-fretboard
-          (if predefined-frets
+          (if predefined-fret-table
               (get-predefined-fretboard
-               predefined-frets
+               predefined-fret-table
                tunings
                pitches)
               '())))
 
-    (set! (ly:grob-property grob 'fret-diagram-details)
-          (if (null? details)
-              (acons 'string-count my-string-count '())
-              (acons 'string-count my-string-count details)))
-
-    (set! (ly:grob-property grob 'dot-placement-list)
-          (if (not (null? predefined-fretboard))
-              predefined-fretboard
-              (let* ((minimum-fret
-                      (ensure-number
-                       (ly:context-property context 'minimumFret)
-                       0))
-                     (max-stretch
-                      (ensure-number
-                       (ly:context-property context 'maximumFretStretch)
-                       4))
-                     (string-frets
-                      (determine-frets-mf
-                       notes
-                       string-numbers
-                       minimum-fret
-                       max-stretch
-                       tunings)))
-               (string-frets->dot-placement
-                 string-frets
-                  my-string-count))))))
-
-(define-public (determine-frets-mf notes string-numbers
-                                  minimum-fret max-stretch
-                                  tunings)
+     (if (null? predefined-fretboard)
+         (let ((string-frets
+                (determine-frets-and-strings
+                 notes
+                 defined-strings
+                 (ly:context-property context 'minimumFret 0)
+                 (ly:context-property context 'maximumFretStretch 4)
+                 tunings)))
+            (if (null? grob)
+                string-frets
+                (create-fretboard
+                 context grob (string-frets->placement-list
+                                string-frets string-count))))
+         (if (null? grob)
+             (placement-list->string-frets predefined-fretboard)
+             (create-fretboard context grob predefined-fretboard)))))
+
+
+(define (determine-frets-and-strings
+          notes defined-strings minimum-fret maximum-stretch tuning)
 
   (define (calc-fret pitch string tuning)
     (- (ly:pitch-semitones pitch) (list-ref tuning (1- string))))
 
       finger-found))
 
-  (define (note-string ev)
-    (let* ((articulations (ly:event-property ev 'articulations))
-          (string-found #f))
-
-      (map (lambda (art)
-            (let* ((num (ly:event-property art 'string-number)))
-
-              (if (number? num)
-                  (set! string-found num))))
-          articulations)
+  (define (string-number event)
+    (let ((num (ly:event-property event 'string-number)))
+      (if (number? num)
+          num
+          #f)))
 
-      string-found))
 
-  (define (del-string string)
+  (define (delete-free-string string)
     (if (number? string)
        (set! free-strings
              (delete string free-strings))))
 
-  (define specified-frets '())
   (define free-strings '())
+  (define unassigned-notes '())
+  (define specified-frets '())
 
   (define (close-enough fret)
-    (reduce
-     (lambda (x y)
-       (and x y))
-     #t
-     (map (lambda (specced-fret)
-           (> max-stretch (abs (- fret specced-fret))))
-         specified-frets)))
+    (if (null? specified-frets)
+        #t
+        (reduce
+          (lambda (x y)
+            (and x y))
+          #t
+          (map (lambda (specced-fret)
+                 (> maximum-stretch (abs (- fret specced-fret))))
+               specified-frets))))
 
   (define (string-qualifies string pitch)
-    (let* ((fret (calc-fret pitch string tunings)))
+    (let* ((fret (calc-fret pitch string tuning)))
       (and (>= fret minimum-fret)
           (close-enough fret))))
 
   (define string-fret-fingering-tuples '())
-  (define (set-fret note string)
-    (set! string-fret-fingering-tuples
-         (cons (list string
-                     (calc-fret (ly:event-property note 'pitch)
-                                string tunings)
-                     (note-finger note))
-               string-fret-fingering-tuples))
-    (del-string string))
-
-
-  ;;; body.
-  (set! specified-frets
-       (filter identity (map
-                         (lambda (note)
-                           (if (note-string note)
-                               (calc-fret (note-pitch note)
-                                          (note-string note) tunings)
-                               #f))
-                         notes)))
-
-  (set! free-strings (map 1+ (iota (length tunings))))
-
-  (for-each (lambda (note)
-             (del-string (note-string note)))
-           notes)
-
 
+  (define (set-fret note string)
+    (let ((this-fret (calc-fret (ly:event-property note 'pitch)
+                                string
+                                tuning)))
+       (set! string-fret-fingering-tuples
+             (cons (list string
+                         this-fret
+                         (note-finger note))
+                   string-fret-fingering-tuples))
+       (delete-free-string string)
+       (set! specified-frets (cons this-fret specified-frets))))
+
+  ;;; body of determine-frets-and-strings
+  (set! free-strings (map 1+ (iota (length tuning))))
+
+  ;; get defined-strings same length as notes
+  (while (< (length defined-strings) (length notes))
+         (set! defined-strings (append defined-strings '(()))))
+
+  ;; handle notes with strings assigned
+  (for-each
+    (lambda (note string)
+      (if (null? string)
+          (set! unassigned-notes (cons note unassigned-notes))
+          (let ((this-string (string-number string)))
+            (delete-free-string this-string)
+            (set-fret note this-string))))
+    notes defined-strings)
+
+  ;; handle notes without strings assigned
   (for-each
    (lambda (note)
-     (if (note-string note)
-        (set-fret note (note-string note))
-        (let* ((fit-string (find (lambda (string)
-                                   (string-qualifies string (note-pitch note)))
-                                 free-strings)))
-          (if fit-string
-              (set-fret note fit-string)
-              (ly:warning "No string for pitch ~a (given frets ~a)"
-                           (note-pitch note)
-                          specified-frets)))))
-   (sort notes note-pitch>?))
-
-  string-fret-fingering-tuples)
-
+     (let* ((fit-string
+              (find (lambda (string)
+                      (string-qualifies string (note-pitch note)))
+                    free-strings)))
+        (if fit-string
+            (set-fret note fit-string)
+            (ly:warning "No string for pitch ~a (given frets ~a)"
+                        (note-pitch note)
+                        specified-frets))))
+   (sort unassigned-notes note-pitch>?))
+
+   string-fret-fingering-tuples)
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; tablature
 
 ;; Calculate the fret from pitch and string number as letter
 ;; The fret letter is taken from 'fretLabels if present
-(define-public (fret-letter-tablature-format string-number context event)
-  (let* ((tuning (ly:context-property context 'stringTunings))
-         (pitch (ly:event-property event 'pitch))
-         (labels (ly:context-property context 'fretLabels))
-         (fret (- (ly:pitch-semitones pitch)
-                  (list-ref tuning (- string-number 1)))))
-    (make-vcenter-markup
-     (cond
-      ((= 0 (length labels))
-       (string (integer->char (+ fret (char->integer #\a)))))
-      ((and (<= 0 fret) (< fret (length labels)))
-       (list-ref labels fret))
-      (else
-       (ly:warning "No label for fret ~a (~a on string ~a);
+(define-public (fret-letter-tablature-format
+                context string-number fret-number)
+ (let ((labels (ly:context-property context 'fretLabels)))
+  (make-vcenter-markup
+   (cond
+    ((= 0 (length labels))
+     (string (integer->char (+ fret-number (char->integer #\a)))))
+    ((and (<= 0 fret-number) (< fret-number (length labels)))
+     (list-ref labels fret-number))
+    (else
+     (ly:warning "No label for fret ~a (on string ~a);
 only ~a fret labels provided"
-                  fret pitch string-number (length labels))
+                 fret-number string-number (length labels))
        ".")))))
 
-;; Calculate the fret from pitch and string number as number
-(define-public (fret-number-tablature-format string-number context event)
-  (let* ((tuning (ly:context-property context 'stringTunings))
-        (pitch (ly:event-property event 'pitch)))
-    (make-vcenter-markup
-     (format
-      "~a"
-      (- (ly:pitch-semitones pitch)
-         (list-ref tuning
-                   ;; remove 1 because list index starts at 0
-                   ;;and guitar string at 1.
-                   (1- string-number)))))))
+;; Display the fret number as a number
+(define-public (fret-number-tablature-format
+                context string-number fret-number)
+  (make-vcenter-markup
+    (format "~a" fret-number)))
 
 ;; The 5-string banjo has got a extra string, the fifth (duh), which
 ;; starts at the fifth fret on the neck.  Frets on the fifth string
@@ -426,17 +431,28 @@ only ~a fret labels provided"
 ;;   the "first fret" on the fifth string is really the sixth fret
 ;;   on the banjo neck.
 ;; We solve this by defining a new fret-number-tablature function:
-(define-public (fret-number-tablature-format-banjo string-number context event)
-  (let* ((tuning (ly:context-property context 'stringTunings))
-        (pitch (ly:event-property event 'pitch)))
-    (make-vcenter-markup
-      (let ((fret (- (ly:pitch-semitones pitch)
-                     (list-ref tuning (1- string-number)))))
-        (number->string (cond
-                          ((and (> fret 0) (= string-number 5))
-                            (+ fret 5))
-                          (else fret)))))))
-
+(define-public (fret-number-tablature-format-banjo
+                context string-number fret-number)
+ (make-vcenter-markup
+  (number->string (cond
+                   ((and (> fret-number 0) (= string-number 5))
+                    (+ fret-number 5))
+                   (else fret-number)))))
+
+;;  Tab note head staff position functions
+;;
+;;  Define where in the staff to display a given string.  Some forms of
+;;  tablature put the tab note heads in the spaces, rather than on the
+;;  lines
+
+(define-public (tablature-position-on-lines context string-number)
+ (let* ((string-tunings (ly:context-property context 'stringTunings))
+        (string-count (length string-tunings))
+        (string-one-topmost (ly:context-property context 'stringOneTopmost))
+        (staff-line (- (* 2 string-number) string-count 1)))
+  (if string-one-topmost
+      (- staff-line)
+      staff-line)))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; bar numbers