summaryrefslogtreecommitdiff
path: root/graphics/pgf/contrib/chordbox/chordbox.sty
blob: b008c01a91d6db2fd468ef6cf1c3420fa983600e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
%
% Copyright 2018 Steven Franzen
%
% This file is part of chordbox.
%
% Chordbox may be distributed and/or modified under the conditions of the
% LaTeX Project Public License, either version 1.3 of this license or (at your
% option) any later version. The latest version of this license is in
% http://www.latex-project.org/lppl.txt and version 1.3 or later is part of all
% distributions of LaTeX version 2005/12/01 or later.
%
% Chordbox has the LPPL maintenance status `maintained'.
%
% The Current Maintainer of chordbox is Steven Franzen.
%
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{chordbox}[2019/04/14 v1.0 Minor improvement to documentation]

\RequirePackage{tikz, xifthen, xstring}
\usetikzlibrary{shapes.misc, scopes, backgrounds}

\newif\ifcb@startbarre

% Set up keys for the drawing code
\pgfqkeys{/chordbox}{
    % This fills a bar of width 0.4 that scales properly, without requiring
    % extra libraries
    draw barre/.code 2 args={%
        \fill (#1) -- +(0, -0.2) -| (#2) -- +(0, 0.2) -| cycle;
    },
    draw barre/.value required,
    % Number of frets displayed
    numfrets/.initial=4,
    % Base fret number parser
    base fret/.code={fr\raisebox{.5ex}{\scriptsize#1}},
    % Chord name parser
    name/.code=\ensuremath{\mathrm{#1}},
    name/.value required,
    % Configuration of text below the chord box and on the nodes
    text below/.is choice,
    text below/none/.code={\def\cb@textbelow{0}},
    text below/fingering/.code={\def\cb@textbelow{1}},
    text below/pitch/.code={\def\cb@textbelow{2}},
    text below/fingering,
    text on node/.is choice,
    text on node/none/.code={\def\cb@textonnode{0}},
    text on node/fingering/.code={\def\cb@textonnode{1}},
    text on node/pitch/.code={\def\cb@textonnode{2}},
    text on node/none,
    % Symbols for pitch notation
    flat symbol/.initial=\flat,
    sharp symbol/.initial=\sharp,
    % Pitch name settings; arrays only seem to work with TeX macros
    pitch names/.store in=\cb@pitchnames,
    pitch names={"A","A\#","B","C","C\#","D","D\#","E","F","F\#","G","G\#"},
    tuning/.store in=\cb@tuning,
    tuning={"E","A","D","G","B","E"},
    % The base fret of a given chord box
    @basefret/.initial=1,
    % Calculate the given fret position #1, corrected for the base fret
    @fretnum/.code=\pgfmathtruncatemacro{\fretnum}{%
        1 + #1 - \pgfkeysvalueof{/chordbox/@basefret}
    },
    % Whether the barre has yet to be started
    @start barre/.is if=cb@startbarre,
    % Find and typeset pitch of string number #1 at fret number #2
    @typeset pitch/.code 2 args={
        \pgfmathparse{{\cb@tuning}[#1-1]}
        \ifnum#2=0
            \pgfkeys{@replace symbols=\pgfmathresult}
        \else
            \foreach \p in {0,...,11} {%
                \pgfmathsetmacro{\stringpitch}{{\cb@pitchnames}[\p]}%
                \if\stringpitch\pgfmathresult
                    \pgfmathparse{{\cb@pitchnames}[mod(\p + #2, 12)]}
                    \pgfkeys{@replace symbols=\pgfmathresult}%
                    \breakforeach
                \fi
            }
        \fi
    },
    % Replace pitch text with math symbols
    @replace symbols/.code={%
        \def\#{\ensuremath{\pgfkeysvalueof{/chordbox/sharp symbol}}}%
        \StrSubstitute{#1}{b}{%
            \ensuremath{\pgfkeysvalueof{/chordbox/flat symbol}}%
        }
    }
}

% Define tikz drawing styles. The muted string symbol size is slightly smaller
% to make it visually similar to the open symbol. Both are also placed above
% their given coordinate for better separation from the box.
\tikzset{
    chordbox/.style={baseline, scale=0.25},
    chordbox/.value forbidden,
    fret node text/.style={font=\Large\bfseries,text=white},
    fret node text/.value forbidden,
    pitch text below/.style={font=\tiny},
    string/.is choice,
    string/base/.style={%
        circle, draw, inner sep=0, minimum size=20, transform shape%
    },
    string/fretted/.style={string/base, fill},
    string/open/.style={string/base, above},
    string/muted/.style={string/open, cross out, minimum size=19}
}

% Environment with parts common to both commands
\newenvironment{chordboxenv}[3]{%
    \pgfqkeys{/chordbox}{@basefret={#1}, numfrets/.get=\numfrets}
    \pgfqkeys{}{.search also=/chordbox}
    % The code that draws the chord box. Argument #1 specifies the base fret
    % (>1) of the chord box, #2 is the chord name and #3 the comma-separated
    % list of finger positions, e.g. {x,0,2:2,2:3,1:1,0}.
    \begin{tikzpicture}[chordbox]%
        % Put coordinates at finger positions and above the frets (index 0),
        % store string count, then draw the string grid and decorations
        \foreach[count=\n] \fretposition in {#3} {
            \foreach \fret in {0,...,\numfrets}
                \coordinate (\n\fret) at (\n - 1, 0.5 - \fret);
            % Extra coordinate for text below each string
            \coordinate (\n) at (\n - 1, -\numfrets);
        }
        \draw grid (\n - 1, -\numfrets);
        \ifthenelse{\isempty{#1}\or#1<2}{% Draw the "nut"
            \fill rectangle (\n - 1, .2);
        }{% Draw the position indicator
            \node[left] at (11) {\pgfkeys{base fret={#1}}};
        }
        % Center chord name over strings
        \pgfmathparse{0.5 * (\n - 1)}
        \node[above] at (\pgfmathresult, 1.25) {\pgfkeys{name={#2}}};
        % Draw the string symbols according to argument #3
        \foreach[count=\s] \fret in {#3} {
            \ifnum\pdfmatch{^[0-9]+}{\fret}=1
                \StrBehind{\pdflastmatch 0}{>}[\num]
                \StrBehind{\fret}{:}[\frettext]
                \def\pitch{\pgfkeys{@typeset pitch={\s}{\num}}}
                \let\nodetext\empty
                \ifcase\cb@textonnode
                \or
                    \let\nodetext\frettext % 1
                \or
                    \let\nodetext\pitch % 2
                \fi
                \ifnum\num=0
                    \node[string=open, fret node text, black] at (\s0)
                        {\nodetext};
                \else
                    \pgfkeys{@fretnum=\num}
                    \node[string=fretted, fret node text] at (\s\fretnum)
                        {\nodetext};
                \fi
                % Display the "text below"
                \ifnum\cb@textbelow>0
                    \if1\cb@textbelow
                        \if\frettext\empty
                        \else
                            \node[below] at (\s) {\frettext};
                        \fi
                    \else
                        % Insert vphantom here to align all pitch names
                        \node[below, pitch text below] at (\s)
                            {\vphantom{\pgfkeys{@replace symbols=\#b}}\pitch};
                    \fi
                \fi
            \else % Anything other than a number is taken to mean "muted"
                \node[string=muted] at (\s0) {};
            \fi
        }
}{%
    \end{tikzpicture}
}

% Draw a chord box without barres, for example:
%   \chordbox{Am}{x,0,2,2,1,0}
%   \chordbox[5]{A}{5,7,7,6,5,5}
\providecommand{\chordbox}[3][1]{%
    \begin{chordboxenv}{#1}{#2}{#3}
    \end{chordboxenv}
}

% Draw a chord box for a barre chord. Extra argument for the fret number(s)
% whose notes should be barred, for example:
%   \bchordbox[3]{C}{x,3,5,5,5,3}{3,5}
\providecommand{\bchordbox}[4][1]{%
    \begin{chordboxenv}{#1}{#2}{#3}
        \foreach \b in {#4} {
            \pgfkeys{@start barre=true, @fretnum=\b}
            \foreach[count=\s] \f in {#3} {
                \if\f\b
                    \ifcb@startbarre
                        \coordinate (start) at (\s\fretnum);
                        \global\cb@startbarrefalse
                    \else
                        \coordinate (end) at (\s\fretnum);
                    \fi
                \fi
            }
            \scoped[on background layer]
                \pgfkeys{draw barre={start}{end}};
        }
    \end{chordboxenv}
}

\endinput
[2018/12/07 v0.3 Support for pitch info]
[2018/12/04 v0.2 Support for fingering info, further customisation]
[2018/12/01 v0.1 Initial release]