[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Rootless slash chords, 2017 edition
From: |
Thomas Morley |
Subject: |
Re: Rootless slash chords, 2017 edition |
Date: |
Tue, 28 Mar 2017 19:49:08 +0200 |
2017-03-28 0:39 GMT+02:00 Dmitry <address@hidden>:
> Hi,
>
> There's been a long standing feature request:
>
> https://sourceforge.net/p/testlilyissues/issues/3909/
>
> #3909 a feature to disable the chord root name printing - just print
> the slash and inversion
>
> Over the years, it has been addressed more or less. Time to give it
> another try?
>
> In a few words, we need a mechanism to suppress printing repeated root
> chord name over a progression of slash chords. That means, we'd like to
> see this:
>
> F#min9 /F /E /D#
>
> instead of this:
>
> F#min9 F#min9/F F#min9/E F#min9/D#
>
> The latter is much less readable, occupies more space and bloats the
> score.
>
> To say this feature is demanded in jazz means say nothing! I've
> recently started working on a bunch of jazz scores, and the very first
> piece features three (!) different progressions with changing bass
> notes, like the above. OK, there is a well-known hack that uses
> \whiteout: http://lsr.di.unimi.it/LSR/Item?id=776
>
> Unfortunately, for the real-life scores it is simply a no-go. It
> requires a chord exception for each and every rootless slash chord, it
> messes up MIDI output (as slash chords are not recognized in chord
> exceptions), not to mention that it produces unclean output.
>
> There is another workaround from 2011: http://www.mail-archive.com/lily
> pond-user%40gnu.org/msg67087.html
> Needless to say, it doesn't work in 2017 :) but I've managed to fix it.
> Here we go,
>
> #(define (rootless-chord-names in-pitches bass inversion context)
> (ignatzek-chord-names `(,(ly:make-pitch 0 0 0) ,(ly:make-pitch 0 0
> 0)) bass inversion context))
>
> #(define (empty-namer pitch lower?) (make-simple-markup ""))
>
> retainChordNoteNamer =
> \applyContext
> #(lambda (context)
> (let ((rn (ly:context-property context 'chordRootNamer)))
> (ly:context-set-property! context 'chordNoteNamer rn)))
>
> rootless = {
> \retainChordNoteNamer
> \once \set chordNameFunction = #rootless-chord-names
> \once \set chordRootNamer = #empty-namer
> }
>
> After that, one can use \rootless as follows:
>
> fis2:m7.9 \rootless fis4:m7.9/f \rootless fis4:m7.9/e \rootless
> fis1:m7.9/dis
>
> This produces perfect output, but the code is ugly as hell. What I'm
> doing here is basically the following:
> - kill off chordRootNamer, but retain chordNoteNamer;
> - after that, the root note note wouldn't be printed, but the suffix
> (like m9, etc.) would be. To suppress it, we supply a
> proxy chordNameFunction that would guarantee a suffix-less chord.
>
> It should be easy to convert this one-shot semantics to something like
> \rootlessOn and \rootlessOff. In fact, it would be nice if mainstream
> Lilypond had something similar to "\set chordChanges = ..." but for
> slash chords; but of course nobody would like to see ugly hacks like
> the above in the mainstream :)
>
> Off the top of my head, I could propose the following solution:
>
> - inside Scheme code for chord name functions ({ignatzek,banter,jazz}-
> chord-names), allow for NIL "pitches" argument. That should mean
> "produce output for bass note only, omitting everything before the
> slash";
> - in Chord_name_engraver::process_music, track repeating root parts of
> slash chords and pass NIL pitches to a chordNameFunction in case of
> repetition. Everything should be similar to handling chordChanges,
> however this time we should remember and compare whole chord
> structures, not markup.
>
> What do you think?
>
> Cheers,
> Dmitry
Hi Dmitry,
below my approch.
I'm afraid you'll not have problems to break it...
Comments inline.
\version "2.19.57"
%% Tries to compare the relevant parts of the markups for current and previous
%% chord.
%%
%% Probably it would be far easier to compare actual pitches in the
%% chordNameFunction as delivered there, but 'lastChord' returns only a markup
%% not pitches.
#(define (test-engraver ctx)
(let ((prev '()))
(make-engraver
(acknowledgers
((chord-name-interface engraver grob source-engraver)
;; As of the 'ignatzek-chord-names'-procedure:
;; 'current' will always be a list, as well as '(last current)'
;; So it seems safe to apply list-modifying procedures to them.
;; Ofcourse this may change, when 'ignatzek-chord-names' is changed
;; or a different chordNameFunction is used.
(let* ((current (ly:grob-property grob 'text))
(current-string-lists
(split-list-by-separator
(filter
(lambda (arg)
(and (string? arg) (not (string-null? arg))))
(flatten-list current))
(lambda (x) (string=? x "/"))))
(prev-string-lists
(split-list-by-separator
(filter
(lambda (arg)
(and (string? arg) (not (string-null? arg))))
(if (pair? prev)
(flatten-list (car prev))
'()))
(lambda (x) (string=? x "/"))))
(current-bass (take-right (last current) 1)))
;; print 'current-bass' if the root of current and previous chord
;; are the same, the bass is different and present at all.
;; Otherwise do nothing.
(if (and (pair? prev)
(equal? (car current-string-lists) (car
prev-string-lists))
(not (equal? (last current-string-lists) (last
prev-string-lists)))
(pair? (cdr current-string-lists)))
(ly:grob-set-property! grob 'text
(make-line-markup
;; reinject "/", which is not caught
;; by 'current-bass'
(cons "/" current-bass))))
(set! prev (cons current prev)))))
((finalize translator)
;; clear 'prev'
(set! prev '())))))
\layout {
\context {
\ChordNames
\consists \test-engraver
}
}
\new ChordNames
\chordmode {
c:m/e c:m/+g c:m/fes c/fes c/d c:7/d c:7/+g d e e:m f:m f:m f
}
\new ChordNames
\chordmode {
c c
c:m7
c:m7/bes
c:m7 c:m7.9/cis
c:m7/cis
}
HTH a bit,
Harm