% \iffalse meta-comment
%<*internal>
\iffalse
%</internal>
%<*readme>
xpiano - An extension of piano.sty by Émile Daneault
====================================================

This package is an extension of piano.sty, written in expl3 
in answer to a couple of questions on TeX.StackExchange

  http://tex.stackexchange.com/questions/162184/
  http://tex.stackexchange.com/questions/246276/

It features extended syntax and several options, like setting
the color, adding numbers for pitch analysis, one or two
octaves and others.

Installation
------------

The package is supplied in .dtx format; run 'tex xpiano.dtx'
for extracting the style file and 'pdflatex xpiano.dtx' for
building the documentation.

Note that this package requires LaTeX3 support.

License
-------

This package is licensed under the LPPL license.
%</readme>
%<*internal>
\fi
\def\nameofplainTeX{plain}
\ifx\fmtname\nameofplainTeX\else
  \expandafter\begingroup
\fi
%</internal>
%<*install>
\input l3docstrip.tex
\keepsilent
\askforoverwritefalse
\preamble
---------------------------------------------------------------
The xpiano package --- Extension of piano.sty by \string\'Emile Daneault
Maintained by Enrico Gregorio
Email: enrico DOT gregorio AT univr DOT it
Released under the LaTeX Project Public License v1.3c or later
See http://www.latex-project.org/lppl.txt
---------------------------------------------------------------
\endpreamble
\postamble
Copyright (C) 2015 by
  Enrico Gregorio <enrico DOT gregorio AT univr DOT it>

It may be distributed and/or modified under the conditions of
the LaTeX Project Public License (LPPL), either version 1.3c of
this license or (at your option) any later version.  The latest
version of this license is in the file:
   http://www.latex-project.org/lppl.txt

This work is "maintained" (as per LPPL maintenance status) by
  Enrico Gregorio.

This work consists of the file  xpiano.dtx
          and the derived files xpiano.pdf,
                                xpiano.sty and
                                xpiano.ins.
\endpostamble
\usedir{tex/latex/xpiano}
\generate{
  \file{\jobname.sty}{\from{\jobname.dtx}{package}}
}
%</install>
%<install>\endbatchfile
%<*internal>
\usedir{source/latex/xpiano}
\generate{
  \file{\jobname.ins}{\from{\jobname.dtx}{install}}
}
\nopreamble\nopostamble
\usedir{doc/latex/xpiano}
\generate{
  \file{README.txt}{\from{\jobname.dtx}{readme}}
}
\ifx\fmtname\nameofplainTeX
  \expandafter\endbatchfile
\else
  \expandafter\endgroup
\fi
%</internal>
%<*driver|package>
\RequirePackage{expl3,xparse}
%</driver|package>
%<*driver>
\documentclass{l3doc}
\usepackage{booktabs,array,xpiano}
%\DisableImplementation
\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</driver>
% \fi
%
%\GetFileInfo{\jobname.sty}
%
%\title{^^A
%  \textsf{xpiano} --- An extension of \pkg{piano.sty} \\
%  (originally written by \'Emile Daneault)\thanks{^^A
%    This file describes \fileversion, last revised \filedate.^^A
%  }^^A
%}
%\author{^^A
%  Enrico Gregorio\thanks
%    {^^A
%      Email: \texttt{enrico DOT gregorio AT univr DOT it}^^A
%    }^^A
%}
%\date{Released \filedate}
%
%\maketitle
%
% \changes{v1.0}{2015/05/23}{First public release}
%
%\begin{abstract}
% The package provides macros for typesetting virtual keyboards
% limited to two octaves, for showing notes represented by a
% colored circle. Optionally the number used for pitch analysis
% can be shown.
%\end{abstract}
%
%\tableofcontents
%
%\begin{documentation}
%
%\section{Introduction}
%
% Simple keyboard representations can be useful for people involved
% in music. This package builds upon \pkg{piano.sty} by \'Emile
% Daneault (available on CTAN), adding several features:
%\begin{itemize}
% \item the number of notes is arbitrary;
% \item the color of the circles is customizable;
% \item the number used for pitch analysis can be shown;
% \item the width of the keys and the length of the black keys
%       are customizable;
% \item it's possible to show one or two octaves, optionally
%       adding a trailing C;
%\end{itemize}
% The package provides the commands
%\begin{itemize}
% \item \cs{keyboard}\oarg{options}\marg{notes}
% \item \cs{keyboardsetup}\marg{options}
% \item \cs{Keyboard} with up to seven optional arguments
%\end{itemize}
% The last command is intended for users of \pkg{piano.sty}
% who can recycle their diagrams by just changing the lowercase
% \cs{keyboard} into \cs{Keyboard} with no other change.
%
%\section{Usage}
%
% The basic command is \cs{keyboard}, which takes as mandatory
% argument the list of notes to mark. The notes can be expressed in
% different ways:
%\begin{itemize}
% \item the convention of \pkg{piano.sty} where they are\\
% \texttt{Co Cso Do Dso Eo Fo Fso Go Gso Ao Aso Bo}\\
% \texttt{Ct Cst Dt Dst Et Ft Fst Gt Gst At Ast Bt}
% \item the German names\\
% \texttt{C~~Ciss~~D~~Diss~~E~~F~~Fiss~~G~~Giss~~A~~Aiss~~B}\\
% \texttt{C'~Ciss' D' Diss' E' F' Fiss' G' Giss' A' Aiss' B'}
% \item the English names\\
% \texttt{C~~Cs~~D~~Ds~~E~~F~~Fs~~G~~Gs~~A~~As~~B}\\
% \texttt{C' Cs' D' Ds' E' F' Fs' G' Gs' A' As' B'}
% \item the ``pitch analysis'' convention\\
% \texttt{0~~1~~2~~3~~4~~5~~6~~7~~8~~9~~10~~11}\\
% \texttt{0' 1' 2' 3' 4' 5' 6' 7' 8' 9' 10' 11'}
%\end{itemize}
% In the lists above, the lines denote the first and second octaves,
% respectively. For the German style names, the enharmonic equivalents
% are also available, \texttt{Dess Ess Gess Ass Bess} (with the primed
% correspondents).
%
% The example in the documentation of \pkg{piano.sty} can so be input
% in any of the following equivalent forms
% \DescribeMacro{\keyboard}\DescribeMacro{\Keyboard}
%\begin{flushleft}\ttfamily
% \cs{Keyboard}[Co][Eo][Gso][Ct][Et]\\
% \cs{keyboard}\{Co,Eo,Gso,Ct,Et\}\\
% \cs{keyboard}\{C,E,Giss,C',E'\}\\
% \cs{keyboard}\{C,E,Gs,C',E'\}\\
% \cs{keyboard}\{0,4,8,0',4'\}
%\end{flushleft}
% producing the following diagram
%\begin{center}
% \keyboard{Co,Eo,Gso,Ct,Et}
%\end{center}
%
% Each \cs{keyboard} command can receive an optional argument, where
% options for drawing the keyboard are set with a key-value syntax.
% The keys are listed in table~\ref{tab-keys}. As usual, the boolean
% valued keys can be simply specified by name, the \texttt{=true}
% value is implicit. The default \texttt{pianodefault} color is
% defined as in \pkg{piano.sty} by \verb|\definecolor{pianodefault}{RGB}{255,127,0}|.
%
%\begin{table}[htp]
% \centering\small
% \caption{The keys for setup}\label{tab-keys}
% \medskip
%\begin{tabular}{>{\ttfamily}lll>{\raggedright\arraybackslash}p{.4\textwidth}}
% \toprule
% Key & Type & Default & Explanation \\
% \midrule
%  color & Literal & \texttt{pianodefault} & The color of the note markers \\
%  single & Boolean & \texttt{false} & Draw just one octave \\
%  ext & Boolean & \texttt{false} & Add a trailing C \\
%  size & Dimension & \texttt{0.5cm} & The width of a white key \\
%  height & Numeric & \texttt{4} & The ratio between height and width of white keys \\
%  ratio & Numeric & \texttt{0.75} & The ratio between the height of the black and the white keys \\
%  numbers & Boolean & \texttt{false} & Show the pitch analysis number \\
%  font & Literal & \cs{tiny} & The font specification for the number \\
%  numbercolor & Literal & \texttt{black} & The color of the numbers \\
%  10 & Literal & \texttt{10} & The representation of the number~10 \\
%  11 & Literal & \texttt{11} & The representation of the number~11 \\
% \bottomrule
%\end{tabular}
%\end{table}
%
% The settings to the key in the optional argument to \cs{keyboard} is
% local to the diagram being drawn. For instance, the following input
% will mark all possible notes in one octave with $t$ and $e$ for ten and eleven as
% numbers:
%\begin{flushleft}\ttfamily
% \cs{keyboard}[\\
% ~~single,\\
% ~~ext,\\
% ~~size=1cm,\\
% ~~font=\cs{small},\\
% ~~numbers,\\
% ~~ratio=0.67,\\
% ~~10=\$t\$, 11=\$e\$,\\
% ]\{C,Cs,D,Ds,E,F,Fs,G,Gs,A,As,B,C'\}
%\end{flushleft}
%\begin{center}
% \keyboard[
%   single,
%   ext,
%   size=1cm,
%   font=\small,
%   numbers,
%   ratio=0.67,
%   10=$t$, 11=$e$,
% ]{C,Cs,D,Ds,E,F,Fs,G,Gs,A,As,B,C'}
%\end{center}
%
% \DescribeMacro{\keyboardsetup}
% The \cs{keyboardsetup} command can be used to set the options from
% one point on; the best place is of course the preamble, so the
% choice will affect all diagrams. However this command obeys the
% usual scoping rules. For instance, if you want most of your diagrams
% to have the pitch numbers and be larger than the standard ones, you
% can say in the preamble
%\begin{flushleft}\ttfamily
% \cs{keyboardsetup}\{\\
% ~~numbers,\\
% ~~size=0.8cm,\\
% ~~ratio=0.67,\\
% ~~font=\cs{small},\\
% ~~10=\$t\$, 11=\$e\$,\\
% \}
%\end{flushleft}
% and specify \texttt{numbers=false} in the optional argument to
% \cs{keyboard} for the occasional diagram which you don't want to be
% numbered.
%
%\section{Cautions}
%
% The \texttt{color} key accepts any color in the syntax of the
% \pkg{xcolor} package. Note that the package is loaded without
% options, so if you want to load it with some options (or load a
% package that does so), you must do it before loading \pkg{xpiano}.
%
%\end{documentation}
%
%\begin{implementation}
%
%\section{Implementation}
%
%    \begin{macrocode}
%<*package>
%    \end{macrocode}
%    
%    \begin{macrocode}
%<@@=xpiano>
%    \end{macrocode}
%
%\subsection{Preliminaries}
% The usual preliminaries, with the declaration of the package and the
% guard against too old versions of \LaTeX3 packages.
%    \begin{macrocode}
\ProvidesExplPackage {xpiano} {2015/05/23} {1.0}
  {An extension of piano.sty by \'Emile Daneault}
\@ifpackagelater { expl3 } { 2015/03/01 }
  { }
  {
    \PackageError { xpiano } { Support~package~expl3~too~old }
      {
        You~need~to~update~your~installation~of~the~bundles~'l3kernel'~and~
        'l3packages'.\MessageBreak
        Loading~xpiano~will~abort!
      }
    \tex_endinput:D
  }
%    \end{macrocode}
% The support package and the definition of the default color
%    \begin{macrocode}
\RequirePackage{xcolor}
\definecolor{pianodefault}{RGB}{255,127,0}
%    \end{macrocode}
%
%\subsection{The user level commands}
%
% \begin{macro}{\keyboard,\keyboardsetup,\Keyboard,\xpiano_keyboard:nn}
% Nothing really complicated: just pass control to inner functions.
%    \begin{macrocode}
\NewDocumentCommand{\keyboard}{ O{}m }
 {
  \xpiano_keyboard:nn { #1 } { #2 }
 }
\NewDocumentCommand{\keyboardsetup}{ m }
 {
  \keys_set:nn { piano } { #1 }
 }
% compatibility with piano.sty (just change \keyboard to \Keyboard)
\NewDocumentCommand{\Keyboard}{O{}O{}O{}O{}O{}}
 {
  \keyboard{#1,#2,#3,#4,#5}
 }
%    \end{macrocode}
% \end{macro}
%
%\subsection{The key-value interface}
% \begin{macro}{
%   \l_@@_font_tl ,
%   \l_@@_single_bool ,
%   \l_@@_ext_bool ,
%   \l_@@_size_dim ,
%   \l_@@_height_tl ,
%   \l_@@_numbers_bool ,
%   \l_@@_color_tl ,
%   \l_@@_numbercolor_tl ,
%   \l_@@_ten ,
%   \l_@@_eleven_tl ,
%   \l_@@_ratio_fp ,
%   \l_@@_width_tl ,
%   \l_@@_blacknote_height_tl
% }
%    \begin{macrocode}
\keys_define:nn { piano }
 {
  font        .tl_set:N   = \l_@@_font_tl,
  single      .bool_set:N = \l_@@_single_bool,
  ext         .bool_set:N = \l_@@_ext_bool,
  size        .dim_set:N  = \l_@@_size_dim,
  height      .tl_set:N   = \l_@@_height_tl,
  numbers     .bool_set:N = \l_@@_numbers_bool,
  color       .tl_set:N   = \l_@@_color_tl,
  numbercolor .tl_set:N   = \l_@@_numbercolor_tl,
  10          .tl_set:N   = \l_@@_ten_tl,
  11          .tl_set:N   = \l_@@_eleven_tl,
  ratio       .fp_set:N   = \l_@@_ratio_fp,
  font        .initial:n  = \tiny,
  single      .initial:n  = false,
  single      .default:n  = true,
  ext         .initial:n  = false,
  ext         .default:n  = true,
  size        .initial:n  = 0.5cm,
  height      .initial:n  = 4,
  numbers     .initial:n  = false,
  numbers     .default:n  = true,
  color       .initial:n  = {pianodefault},
  numbercolor .initial:n  = black,
  10          .initial:n  = 10,
  11          .initial:n  = 11,
  ratio       .initial:n  = 0.75,
 }
\tl_new:N \l_@@_width_tl
\tl_new:N \l_@@_blacknote_height_tl
%    \end{macrocode}
% \end{macro}
%
% \subsection{The note names}
% 
% We want to allow for several input conventions, so each possible
% note name is converted into an internal numeric representation.
% Using a property list variable is the best strategy.
% \begin{macro}{\g_@@_notes_prop}
%    \begin{macrocode}
\prop_new:N \g_@@_notes_prop
% Daneault convention
\prop_gput:Nnn \g_@@_notes_prop { Co }  { 0 }
\prop_gput:Nnn \g_@@_notes_prop { Cso } { 1 }
\prop_gput:Nnn \g_@@_notes_prop { Do }  { 2 }
\prop_gput:Nnn \g_@@_notes_prop { Dso } { 3 }
\prop_gput:Nnn \g_@@_notes_prop { Eo }  { 4 }
\prop_gput:Nnn \g_@@_notes_prop { Fo }  { 5 }
\prop_gput:Nnn \g_@@_notes_prop { Fso } { 6 }
\prop_gput:Nnn \g_@@_notes_prop { Go }  { 7 }
\prop_gput:Nnn \g_@@_notes_prop { Gso } { 8 }
\prop_gput:Nnn \g_@@_notes_prop { Ao }  { 9 }
\prop_gput:Nnn \g_@@_notes_prop { Aso } { 10 }
\prop_gput:Nnn \g_@@_notes_prop { Bo }  { 11 }
\prop_gput:Nnn \g_@@_notes_prop { Ct }  { 0' }
\prop_gput:Nnn \g_@@_notes_prop { Cst } { 1' }
\prop_gput:Nnn \g_@@_notes_prop { Dt }  { 2' }
\prop_gput:Nnn \g_@@_notes_prop { Dst } { 3' }
\prop_gput:Nnn \g_@@_notes_prop { Et }  { 4' }
\prop_gput:Nnn \g_@@_notes_prop { Ft }  { 5' }
\prop_gput:Nnn \g_@@_notes_prop { Fst } { 6' }
\prop_gput:Nnn \g_@@_notes_prop { Gt }  { 7' }
\prop_gput:Nnn \g_@@_notes_prop { Gst } { 8' }
\prop_gput:Nnn \g_@@_notes_prop { At }  { 9' }
\prop_gput:Nnn \g_@@_notes_prop { Ast } { 10' }
\prop_gput:Nnn \g_@@_notes_prop { Bt }  { 11' }
% German (?) convention
\prop_gput:Nnn \g_@@_notes_prop { C }     { 0 }
\prop_gput:Nnn \g_@@_notes_prop { Ciss }  { 1 }
\prop_gput:Nnn \g_@@_notes_prop { Dess }  { 1 }
\prop_gput:Nnn \g_@@_notes_prop { D }     { 2 }
\prop_gput:Nnn \g_@@_notes_prop { Diss }  { 3 }
\prop_gput:Nnn \g_@@_notes_prop { Ess }   { 3 }
\prop_gput:Nnn \g_@@_notes_prop { E }     { 4 }
\prop_gput:Nnn \g_@@_notes_prop { F }     { 5 }
\prop_gput:Nnn \g_@@_notes_prop { Fiss }  { 6 }
\prop_gput:Nnn \g_@@_notes_prop { Gess }  { 6 }
\prop_gput:Nnn \g_@@_notes_prop { G }     { 7 }
\prop_gput:Nnn \g_@@_notes_prop { Giss }  { 8 }
\prop_gput:Nnn \g_@@_notes_prop { Ass }   { 8 }
\prop_gput:Nnn \g_@@_notes_prop { A }     { 9 }
\prop_gput:Nnn \g_@@_notes_prop { Aiss }  { 10 }
\prop_gput:Nnn \g_@@_notes_prop { Bess }  { 10 }
\prop_gput:Nnn \g_@@_notes_prop { B }     { 11 }
\prop_gput:Nnn \g_@@_notes_prop { C' }    { 0' }
\prop_gput:Nnn \g_@@_notes_prop { Ciss' } { 1' }
\prop_gput:Nnn \g_@@_notes_prop { Dess' } { 1' }
\prop_gput:Nnn \g_@@_notes_prop { D' }    { 2' }
\prop_gput:Nnn \g_@@_notes_prop { Diss' } { 3' }
\prop_gput:Nnn \g_@@_notes_prop { Ess' }  { 3' }
\prop_gput:Nnn \g_@@_notes_prop { E' }    { 4' }
\prop_gput:Nnn \g_@@_notes_prop { F' }    { 5' }
\prop_gput:Nnn \g_@@_notes_prop { Fiss' } { 6' }
\prop_gput:Nnn \g_@@_notes_prop { Gess' } { 6' }
\prop_gput:Nnn \g_@@_notes_prop { G' }    { 7' }
\prop_gput:Nnn \g_@@_notes_prop { Giss' } { 8' }
\prop_gput:Nnn \g_@@_notes_prop { Ass' }  { 8' }
\prop_gput:Nnn \g_@@_notes_prop { A' }    { 9' }
\prop_gput:Nnn \g_@@_notes_prop { Aiss' } { 10' }
\prop_gput:Nnn \g_@@_notes_prop { Bess' } { 10' }
\prop_gput:Nnn \g_@@_notes_prop { B' }    { 11' }
% English (?) convention
\prop_gput:Nnn \g_@@_notes_prop { C }   { 0 }
\prop_gput:Nnn \g_@@_notes_prop { Cs }  { 1 }
\prop_gput:Nnn \g_@@_notes_prop { D }   { 2 }
\prop_gput:Nnn \g_@@_notes_prop { Ds }  { 3 }
\prop_gput:Nnn \g_@@_notes_prop { E }   { 4 }
\prop_gput:Nnn \g_@@_notes_prop { F }   { 5 }
\prop_gput:Nnn \g_@@_notes_prop { Fs }  { 6 }
\prop_gput:Nnn \g_@@_notes_prop { G }   { 7 }
\prop_gput:Nnn \g_@@_notes_prop { Gs }  { 8 }
\prop_gput:Nnn \g_@@_notes_prop { A }   { 9 }
\prop_gput:Nnn \g_@@_notes_prop { As }  { 10 }
\prop_gput:Nnn \g_@@_notes_prop { B }   { 11 }
\prop_gput:Nnn \g_@@_notes_prop { C' }  { 0' }
\prop_gput:Nnn \g_@@_notes_prop { Cs' } { 1' }
\prop_gput:Nnn \g_@@_notes_prop { D' }  { 2' }
\prop_gput:Nnn \g_@@_notes_prop { Ds' } { 3' }
\prop_gput:Nnn \g_@@_notes_prop { E' }  { 4' }
\prop_gput:Nnn \g_@@_notes_prop { F' }  { 5' }
\prop_gput:Nnn \g_@@_notes_prop { Fs' } { 6' }
\prop_gput:Nnn \g_@@_notes_prop { G' }  { 7' }
\prop_gput:Nnn \g_@@_notes_prop { Gs' } { 8' }
\prop_gput:Nnn \g_@@_notes_prop { A' }  { 9' }
\prop_gput:Nnn \g_@@_notes_prop { As' } { 10' }
\prop_gput:Nnn \g_@@_notes_prop { B' }  { 11' }
% Pitch analysis convention
\prop_gput:Nnn \g_@@_notes_prop { 0 }   { 0 }
\prop_gput:Nnn \g_@@_notes_prop { 1 }   { 1 }
\prop_gput:Nnn \g_@@_notes_prop { 2 }   { 2 }
\prop_gput:Nnn \g_@@_notes_prop { 3 }   { 3 }
\prop_gput:Nnn \g_@@_notes_prop { 4 }   { 4 }
\prop_gput:Nnn \g_@@_notes_prop { 5 }   { 5 }
\prop_gput:Nnn \g_@@_notes_prop { 6 }   { 6 }
\prop_gput:Nnn \g_@@_notes_prop { 7 }   { 7 }
\prop_gput:Nnn \g_@@_notes_prop { 8 }   { 8 }
\prop_gput:Nnn \g_@@_notes_prop { 9 }   { 9 }
\prop_gput:Nnn \g_@@_notes_prop { 10 }  { 10 }
\prop_gput:Nnn \g_@@_notes_prop { 11 }  { 11 }
\prop_gput:Nnn \g_@@_notes_prop { 0' }  { 0' }
\prop_gput:Nnn \g_@@_notes_prop { 1' }  { 1' }
\prop_gput:Nnn \g_@@_notes_prop { 2' }  { 2' }
\prop_gput:Nnn \g_@@_notes_prop { 3' }  { 3' }
\prop_gput:Nnn \g_@@_notes_prop { 4' }  { 4' }
\prop_gput:Nnn \g_@@_notes_prop { 5' }  { 5' }
\prop_gput:Nnn \g_@@_notes_prop { 6' }  { 6' }
\prop_gput:Nnn \g_@@_notes_prop { 7' }  { 7' }
\prop_gput:Nnn \g_@@_notes_prop { 8' }  { 8' }
\prop_gput:Nnn \g_@@_notes_prop { 9' }  { 9' }
\prop_gput:Nnn \g_@@_notes_prop { 10' } { 10' }
\prop_gput:Nnn \g_@@_notes_prop { 11' } { 11' }
%    \end{macrocode}
% \end{macro}
%
%\subsection{The main function}
%
% \begin{macro}{\xpiano_keyboard:nn}
% The main function opens a group in order not to clobber the default
% values for the keys; then the local settings are looked at and the
% number of keys to draw is set according to the request of one or two
% octaves and the possible trailing~C\@. The vertical position for the
% black notes is stored in a token list for efficiency.
%    \begin{macrocode}
\cs_new_protected:Npn \xpiano_keyboard:nn #1 #2
 {
  \group_begin:
  \keys_set:nn { piano } { #1 }
  \bool_if:NTF \l_@@_ext_bool
   {
    \tl_set:Nx \l_@@_width_tl
     {
      \bool_if:NTF \l_@@_single_bool { 8 } { 15 }
     }
   }
   {
    \tl_set:Nx \l_@@_width_tl
     {
      \bool_if:NTF \l_@@_single_bool { 7 } { 14 }
     }
   }
  \tl_set:Nx \l_@@_blacknote_height_tl
   {
    \fp_eval:n {0.5+(1-\l_@@_ratio_fp)*\l_@@_height_tl}
   }
%    \end{macrocode}
% After the preliminary, the unit length is set and a \texttt{picture}
% is started and the keys are drawn.
%    \begin{macrocode}
  %% Draw the keyboard
  \setlength{\unitlength}{\l_@@_size_dim}
  \begin{picture}(\l_@@_width_tl,\l_@@_height_tl)
  % White keys
  \multiput(0,0)(1,0){\l_@@_width_tl}{\line(0,1){\l_@@_height_tl}}

  % Boundary
  \put(0,0){\line(0,1){\l_@@_height_tl}}
  \put(0,0){\line(1,0){\l_@@_width_tl}}
  \put(\l_@@_width_tl,0){\line(0,1){\l_@@_height_tl}}
  \put(0,\l_@@_height_tl){\line(1,0){\l_@@_width_tl}}

  % Black keys
  \linethickness{.6\l_@@_size_dim}
  \multiput(1,\l_@@_height_tl)(1,0){2}
   {
    \line(0,-1){\fp_eval:n {\l_@@_ratio_fp*\l_@@_height_tl}}
   }
  \multiput(4,\l_@@_height_tl)(1,0){3}
   {
    \line(0,-1){\fp_eval:n {\l_@@_ratio_fp*\l_@@_height_tl}}
   }
  \bool_if:NF \l_@@_single_bool
   {
    \multiput(8,\l_@@_height_tl)(1,0){2}
     {
      \line(0,-1){\fp_eval:n {\l_@@_ratio_fp*\l_@@_height_tl}}
     }
    \multiput(11,\l_@@_height_tl)(1,0){3}
     {
      \line(0,-1){\fp_eval:n {\l_@@_ratio_fp*\l_@@_height_tl}}
     }
   }
%    \end{macrocode}
% Now the notes are drawn; the color is set to the desired one and the
% second argument to \cs{piano_keyboard:nn} is mapped as a comma
% separated list, passing control to an auxiliary function, for
% simplicity. Finally the picture is finished and the group is closed.
%    \begin{macrocode}
  % The notes
  \color{\l_@@_color_tl}
  \clist_map_inline:nn { #2 }
   {
    \@@_do_key:n { ##1 }
   }
  \end{picture}
  \group_end:
 }
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\@@_add_note:nn,\@@_do_key:n}
% The auxiliary function mentioned before converts the input into the
% internal representation using \cs{prop_item:Nn} and doing a
% \cs{str_case:nn} test. Another auxiliary function does the actual
% drawing, just in order not to complicate the code in
% \cs{str_case:fn}.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_add_note:nn #1 #2
 {
  \put(#2){\circle*{0.5}}
  \bool_if:NT \l_@@_numbers_bool
   {
    \put(#2)
     {
      \makebox(0,0)
       {
        \normalfont\color{\l_@@_numbercolor_tl}\l_@@_font_tl #1
       }
     }
   }
 }

\cs_new_protected:Npn \@@_do_key:n #1
 {
  \str_case:fn { \prop_item:Nn \g_@@_notes_prop {#1} }
   {
    {0}{\@@_add_note:nn {0}{0.5,0.5}}
    {2}{\@@_add_note:nn {2}{1.5,0.5}}
    {4}{\@@_add_note:nn {4}{2.5,0.5}}
    {5}{\@@_add_note:nn {5}{3.5,0.5}}
    {7}{\@@_add_note:nn {7}{4.5,0.5}}
    {9}{\@@_add_note:nn {9}{5.5,0.5}}
    {11}{\@@_add_note:nn {\l_@@_eleven_tl}{6.5,0.5}}
    {0'}{\@@_add_note:nn {0}{7.5,0.5}}
    {2'}{\@@_add_note:nn {2}{8.5,0.5}}
    {4'}{\@@_add_note:nn {4}{9.5,0.5}}
    {5'}{\@@_add_note:nn {5}{10.5,0.5}}
    {7'}{\@@_add_note:nn {7}{11.5,0.5}}
    {9'}{\@@_add_note:nn {9}{12.5,0.5}}
    {11'}{\@@_add_note:nn {\l_@@_eleven_tl}{13.5,0.5}}
    {1}{\@@_add_note:nn {1}{1,\l_@@_blacknote_height_tl}}
    {3}{\@@_add_note:nn {3}{2,\l_@@_blacknote_height_tl}}
    {6}{\@@_add_note:nn {6}{4,\l_@@_blacknote_height_tl}}
    {8}{\@@_add_note:nn {8}{5,\l_@@_blacknote_height_tl}}
    {10}{\@@_add_note:nn {\l_@@_ten_tl}{6,\l_@@_blacknote_height_tl}}
    {1'}{\@@_add_note:nn {1}{8,\l_@@_blacknote_height_tl}}
    {3'}{\@@_add_note:nn {3}{9,\l_@@_blacknote_height_tl}}
    {6'}{\@@_add_note:nn {6}{11,\l_@@_blacknote_height_tl}}
    {8'}{\@@_add_note:nn {8}{12,\l_@@_blacknote_height_tl}}
    {10'}{\@@_add_note:nn {\l_@@_ten_tl}{13,\l_@@_blacknote_height_tl}}
   }
 }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\str_case:fn}
% We also need a variant of \cs{str_case:nn}
%    \begin{macrocode}
\cs_generate_variant:Nn \str_case:nn {f}
%    \end{macrocode}
% \end{macro}
% That's all, folks!
%    \begin{macrocode}
%</package>
%    \end{macrocode}
%
%\end{implementation}
%
% \PrintChanges
%
% \PrintIndex