6

This and this answer provide a beautiful code to draw a piano with it's range. I tried to learn them to draw a simple and plain piano diagram with key-names (A, B, C, C#) written on the keys, but the code is nested with other requirements so much that I couldn't make any sense of it. Can anybody help me to understand the code and derive a simple piano diagram out of it?

Code -

\documentclass[border=0.25cm]{standalone}
\usepackage{etex}
\usepackage{tikz}
\usepackage{musixtex}
\usetikzlibrary{fit}
\begin{document}

\begin{tikzpicture}

\def\lastnotename{origin}
\newbox\notebox
\coordinate (origin) at (0,0);
\coordinate (stave) at (origin);
\foreach \octave [evaluate={\t=int(\octave*7-7);}] in {0, ..., 5}{
    \foreach \pitch [count=\c from 0, evaluate={\x=int(\octave*7+\c+1);}] in {A,...,G}{
        \ifnum\t>6
            \tikzset{extract anchor/.style={anchor=south west, at=(\lastnotename.south east)}}
        \else
            \tikzset{extract anchor/.style={anchor=north west, at=(\lastnotename.north east)}}
        \fi
        \edef\notename{\pitch-\octave}  
        \node (\notename)  [inner sep=0pt, outer sep=0pt,text width=1cm, extract anchor/.try]  {%
            \begin{music}
                \instrumentnumber{1}
                \instrumentnumber{2}
                \nostartrule        
                \setstaffs1{1}
                \setstaffs2{1}  
                \ifnum\x>1
                    \setclefsymbol1{\empty}
                    \setclefsymbol2{\empty}
                \fi
                \setclef1{\bass}
                \setclef2{\treble}                                  
                \startextract
                \transpose\t
                \ifnum\t>7
                    \ifnum\t>14
                        \Notes \nextinstrument \ql{\pitch} \en      
                    \else
                        \Notes \nextinstrument \qu{\pitch} \en                      
                    \fi
                \else
                    \Notes \qu{\pitch} \en
                \fi
                \zendextract
            \end{music}};
            \xdef\lastnotename{\pitch-\octave}
}}

\node (stave) [fit={(A-0) (G-5)}] {};

\newif\ifblacknote
\foreach \octave in {0,...,5}
    \foreach \pitch [count=\p, evaluate={\t={"la", "si", "so", "r\`e","mi", "fa", "sol"}[\p-1];}] in {A,...,G}{
        \node [anchor=base] at ([xshift=0.25cm, yshift=-0.25cm]stave.south -| \pitch-\octave.south) {\t};
        \draw ([xshift=0.25cm, yshift=-1cm]stave.south -| \pitch-\octave.south west) rectangle ++(1cm,-4cm);
        \blacknotefalse
        \ifcase\p
        \or
            \blacknotetrue
        \or
        \or
            \blacknotetrue
        \or
            \blacknotetrue
        \or
        \or
            \blacknotetrue
        \or
            \ifnum\octave<5
                \blacknotetrue
            \fi
        \else
        \fi
        \ifblacknote
            \fill ([xshift=0.25cm, yshift=-1cm]stave.south -| \pitch-\octave.south east) ++(-0.25cm,0) rectangle ++(0.5cm,-2.5cm);
        \fi
    }

\end{tikzpicture}
\end{document}
Niranjan
  • 3,435

2 Answers2

9

If I understand the requirements correctly, the musictex part can be stripped out. To simplify further, the coordinates of the keys can be calculated using a regular double for loop over 5 octaves and 7 pitches. The letters from the pitch array can be printed on the key using the calculated x coordinate, and similar for the black keys with a sharp added to the pitch letter.

What remains is some fiddling with the positioning of the keys and pitch labels. Note that, because a new key in the original code was drawn as a rectangle, the left line of the rectangle would overlap the right line of the previous key. Normally this is not a problem, however when the note name is printed in white on the black key then the next white key would be visible on top of the note name. To counter that the white keys are drawn as three lines, only top, right, and bottom. For the very first key an additional left line is drawn before the loop.

MWE:

\documentclass[border=0.25cm]{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}
\begin{document}

\begin{tikzpicture}

\coordinate (origin) at (0,0);
\coordinate (stave) at (origin);
% left line of first key
\draw (0.25,-1) -- (0.25,-5);

\newif\ifblacknote
\foreach \octave in {0,...,5}
    \foreach \pitch [count=\p] in {A,...,G}{
        % calculate x position from octave and pitch
        \pgfmathparse{\octave*7+\p+0.25}
        \edef\myx{\pgfmathresult}
        % draw three lines for top, right, bottom of this key
        \draw (\myx,-1) -- (\myx,-5);
        \draw (\myx,-1) -- ($(\myx,-1)+(-1,0)$);
        \draw (\myx,-5) -- ($(\myx,-5)+(-1,0)$);
        % print pitch on line
        \node [anchor=base,xshift=-15] at (\pgfmathresult,-4.5) {\pitch};
        \blacknotefalse
        \ifcase\p
        \or
            \blacknotetrue
        \or
        \or
            \blacknotetrue
        \or
            \blacknotetrue
        \or
        \or
            \blacknotetrue
        \or
            \ifnum\octave<5
                \blacknotetrue
            \fi
        \else
        \fi
        \ifblacknote
            % recalculate x
            \pgfmathparse{\octave*7+\p}
                \fill ([xshift=0.25cm, yshift=-1cm]stave.south -| \pgfmathresult,0) ++(-0.25cm,0) rectangle ++(0.5cm,-2.5cm);
                % print pitch on black key
                \node [anchor=base,xshift=0.25cm,white] at (\pgfmathresult,-2.5) {\textbf{\pitch}${}^\sharp$};
        \fi
    }

\end{tikzpicture}
\end{document}

Result (partial screenshot):

enter image description here

Edit: added flats on the black keys. Code:

% recalculate x
\pgfmathparse{\octave*7+\p}
\edef\myx{\pgfmathresult}
% calculate flats
\pgfmathparse{array({"B","C","D","E","F","G","A"},\p-1)}
\edef\nextnote{\pgfmathresult}
\fill ([xshift=0.25cm, yshift=-1cm]stave.south -| \myx,0) ++(-0.25cm,0) rectangle ++(0.5cm,-2.5cm);
% print pitch on black key
\node [anchor=base,xshift=0.25cm,white] at (\myx,-2.5) {\textbf{\pitch}${}^\sharp$};
\node [anchor=base,xshift=0.25cm,white] at (\myx,-3.0) {\textbf{\nextnote}${}^\flat$};

enter image description here


Edit: one octave, different characters for note names.

\documentclass[border=0.5cm]{standalone}
\usepackage{fontspec}
\setmainfont[Script=Devanagari,Mapping=devanagarinumerals]{Shobhika}
\usepackage{tikz}
\usetikzlibrary{calc}
\usepackage{ulem}
\begin{document}

\begin{tikzpicture}

\coordinate (origin) at (0,0);
\coordinate (stave) at (origin);
% left line of first key
\draw (0.25,-1) -- (0.25,-5);

\newif\ifblacknote
 \foreach \pitch [count=\p] in {सा,रे,ग,म,प,ध,नी}{
     % calculate x position from octave and pitch
     \pgfmathparse{\p+0.25}
     \edef\myx{\pgfmathresult}
     % draw three lines for top, right, bottom of this key
     \draw (\myx,-1) -- (\myx,-5);
     \draw (\myx,-1) -- ($(\myx,-1)+(-1,0)$);
     \draw (\myx,-5) -- ($(\myx,-5)+(-1,0)$);
     % print pitch on line
     \node [anchor=base,xshift=-15] at (\pgfmathresult,-4.5) {\pitch};
     \blacknotefalse
     \ifcase\p
     \or
         \blacknotetrue
     \or
         \blacknotetrue
     \or
     \or
         \blacknotetrue
     \or
         \blacknotetrue
     \or
         \blacknotetrue
     \or
     \else
     \fi
     \ifblacknote
         \fill ([xshift=0.25cm, yshift=-1cm]stave.south -| \p,0) ++(-0.25cm,0) rectangle ++(0.5cm,-2.5cm);
         % print pitch on black key
         \pgfmathparse{array({"\underline{रे}","\underline{ग}",,"मऺ","\underline{ध}","\underline{नी}"},\p-1)}
            \edef\nextnote{\pgfmathresult}
         \node [anchor=base,xshift=0.25cm,white] at (\p,-2.5) {\nextnote};
     \fi
 }

\end{tikzpicture}
\end{document}

3

Niranjan
  • 3,435
Marijn
  • 37,699
  • 1
    +1 Not TeX related, but labeling black keys as necessarily denoting a sharp isn't really correct. Their label is key dependent. If the diagram is to be used as a general diagram it would be much better to have both the sharp and the corresponding flat on the black keys. (Not your problem, but something the OP might want to bear in mind.) – Alan Munn Feb 04 '20 at 14:31
  • @AlanMunn I've added flats. – Marijn Feb 04 '20 at 14:38
  • 2
    Now I feel much calmer. :) – Alan Munn Feb 04 '20 at 14:38
  • Great. I am interested in writing the pitches in a different script. So the {A,...,G} might not work :( – Niranjan Feb 04 '20 at 14:40
  • Instead of doing this with a for loop can we simply draw one octave with all the pitch labels typed manually? – Niranjan Feb 04 '20 at 14:41
  • @Niranjan It might work (no idea) but if it doesn't you can provide an explicit list; just don't use the ... notation. So instead of {A,...,G} write {A, B, C, D, E, F, G}. – Alan Munn Feb 04 '20 at 14:41
  • Ok. What should be done to reduce this full 7 octave piano to a single octave one? I tried removing *7 from two places, it gave me one octave, but the labels were overwritten. – Niranjan Feb 04 '20 at 14:44
  • the easiest way is to put \foreach \octave in {0,1} instead of \foreach \octave in {0,...,5} and also to adjust \ifnum\octave<5 \blacknotetrue to \ifnum\octave<1 \blacknotetrue. A bit cleaner would be to remove the outer loop and cut all the \octave variables out of the calculations. Note in that case that it would be nicer to start at C instead of at A. – Marijn Feb 04 '20 at 14:47
  • Understood the first part, it worked too, but the labels are getting overwritten. Do you want me to provide the MWE? – Niranjan Feb 04 '20 at 14:51
  • @Niranjan sure, you can edit it into your question. I don't have time at the moment to look at it but I'll try later. – Marijn Feb 04 '20 at 14:53
  • Also I see that the key-label on a preceding white key is directly duplicated on the black key. This is good for the English key-labeling system, but for example in my case we have different labels for white keys and different ones for the white keys. Shall I list them all chronologically in \foreach \pitch? or write two different variables for black keys and white keys? – Niranjan Feb 05 '20 at 12:59
  • So to cut it short let's assume that I have a system where if we start from A key the next black key is B, next white key will be C and so on... How to acomodate it in this model? – Niranjan Feb 05 '20 at 13:13
  • @Niranjan I have added code for different note names. I'm not sure this is the system you intended, and I'm also not sure if I understand how these names work - but the idea is indeed that you make a second array for the black keys and take the names from there. – Marijn Feb 05 '20 at 13:57
  • Woah! I am amazed. You gave me exactly what I was trying to form. I was avoiding the use of Devanagari as it is unknown to many of the users, but this is amazing!! Thank you so much :) – Niranjan Feb 05 '20 at 14:02
5

If you take a very close look at a musical keyboard (piano, organ, harpsichord, etc.), you may be surprised by what you see there: in any group of seven adjacent natural (white) keys, no two have the same shape. True, the C and E keys are mirror images of each other (as are the F and B keys), but none are interchangeable among seven adjacent natural keys. In fact, the dimensions and placement of the keys comprises an interesting and very practical (though unexpected, perhaps) example of linear programming. There are some useful discussions about this on the web: https://www.mathpages.com/home/kmath043.htm, http://www.quadibloc.com/other/cnv05.htm and http://datagenetics.com/blog/may32016/index.html.

The following comes from http://datagenetics.com/blog/may32016/index.html. The code is fairly straightforward (thank goodness for TikZ! -- my very first drawings were done using pencils and rulers with the aid of a slide rule) allowing for relatively easy editing and experimentation. There are countless other ways of accomplishing this, though this one has the advantage that all of the black keys have the same width, and all of the white (natural) keys have the same width -- there are some keyboard layouts for which this is not necessarily true.

\documentclass[tikz,border=3mm]{standalone}

\usepackage{xparse}

\usetikzlibrary{calc}

\newlength{\CtoBwd}%% Width of 7 adjacent white (natural) keys \newlength{\Whitewd}%% Width of a natural key \newlength{\Blackwd}%% Width of a black key \newlength{\Whitefrontht}%% Distance from front of white key to black key \newlength{\Backwdi}%% <<-- See below \newlength{\Backwdii}%% <<-- See below \newlength{\Backwdiii}%% <<-- See below \newlength{\FronttoBack}%% Total length of a natural key \newlength{\Blackht}%% Length of a black key

%% http://datagenetics.com/blog/may32016/index.html

\NewDocumentCommand{\drawaccidental}{mmmm}{% Lower left, sharp name, flat name, coordinate name for upper right of accidental \draw[fill=black] (#1) rectangle node[white,text width=\Backwdii,align=center] {\bfseries\huge#2${}^\sharp$\[1ex]#3${}^\flat$} ++ (\Blackwd,\Blackht)coordinate (#4); }

\NewDocumentCommand{\drawname}{m}{% Coordinate name of lower left of key and name of key \node at ($(#1) + (\Whitewd/2,\Whitefrontht/2)$) {\bfseries\Huge#1}; }

%% f b = \Backwdi %% cs ds fs gs as = \Backwdii %% d g a = \Backwdii %% c e = \Backwdiii

\NewDocumentCommand{\drawkeyboard}{}{% \draw (0,0)coordinate(C) -- ++(0,\FronttoBack) -- ++(\Backwdiii,0) -- ++(0,-\Blackht)coordinate(CS) -| (\Whitewd,0)coordinate(D) -- cycle; \drawaccidental{CS}{C}{D}{CS0} \drawname{C}

\draw (D) -- ++(0,\Whitefrontht) -| (CS0) -- ++(\Backwdii,0) -- ++(0,-\Blackht)coordinate(DS)
    -| (2\Whitewd,0)coordinate(E) -- cycle;
\drawaccidental{DS}{D}{E}{DS0}
\drawname{D}

\draw (E) -- ++(0,\Whitefrontht) -| (DS0) -- ++(\Backwdiii,0) -- ++(0,-\FronttoBack)coordinate(F)
    -- cycle;
\drawname {E}

\draw (F) -- ++(0,\FronttoBack) -- ++(\Backwdi,0) -- ++(0,-\Blackht)coordinate(FS)
    -| (4\Whitewd,0)coordinate(G) -- cycle;
\drawaccidental{FS}{F}{G}{FS0}
\drawname{F}

\draw (G) -- ++(0,\Whitefrontht) -| (FS0) -- ++(\Backwdii,0) -- ++(0,-\Blackht)coordinate(GS)
    -| (5\Whitewd,0)coordinate(A) -- cycle;
\drawaccidental{GS}{G}{A}{GS0}
\drawname{G}

\draw (A) -- ++(0,\Whitefrontht) -| (GS0) -- ++(\Backwdii,0) -- ++(0,-\Blackht)coordinate(AS)
-| (6\Whitewd,0)coordinate(B) -- cycle;
\drawaccidental{AS}{A}{B}{AS0}
\drawname{A}

\draw (B) -- ++(0,\Whitefrontht) -| (AS0) -- ++(\Backwdi,0) -- ++(0,-\FronttoBack) -- cycle;
\drawname{B}

}

\begin{document}

\setlength{\CtoBwd}{6.5in} %% This varies between 160mm to 167mm depending upon manufacturer \pgfmathsetlengthmacro{\tmpwd}{\CtoBwd/5880}\typeout{\tmpwd!!!!} \setlength{\Whitewd}{\dimexpr \tmpwd840\relax} \setlength{\Blackwd}{\dimexpr \tmpwd490\relax} \setlength{\Blackht}{3.75in} \setlength{\Whitefrontht}{2in} \setlength{\FronttoBack}{\dimexpr \Whitefrontht + \Blackht\relax} \setlength{\Backwdi}{\dimexpr\tmpwd455\relax} \setlength{\Backwdii}{\dimexpr\tmpwd490\relax} \setlength{\Backwdiii}{\dimexpr\tmpwd*525\relax}

\begin{tikzpicture}[rounded corners=4pt] \foreach \oct in {0,...,3}{% for 4 octaves; vary at will \begin{scope}[xshift=\oct*\CtoBwd] \drawkeyboard \end{scope} } \end{tikzpicture}

\end{document}

One octave Four octaves

I have number of these illustrations using other layouts which, originally, I prepared to assist my keyboard students.

muzimuzhi Z
  • 26,474
sgmoye
  • 8,586