9

I want to draw blank music staves using nothing but \rule in a loop. (I know there is MusiXTeX and Lilypond, not to mention Tikz, but really, I just want some horizontal lines.)

In this example, the first command \musicstaff successfully draws the staff lines, though an awkward negative vertical adjustment is needed between each.

The second command, \musicstaves, however, fails to loop and create multiple staves. If I substitute the text STAFF for \musicstaff in the body of the loop, it does work, though. What am I missing?

\documentclass{article}

\newcounter{stafflines}
\newcommand{\musicstaff}{%                                                                                                                                     
  \setcounter{stafflines}{0}                                                                                                                                   
  \noindent                                                                                                                                                    
  \loop                                                                                                                                                        
    \rule{\linewidth}{0.4pt}%                                                                                                                                  
    \vspace*{-0.5\baselineskip}                                                                                                                                
    \stepcounter{stafflines}                                                                                                                                   
    \ifnum\value{stafflines} < 5                                                                                                                               
  \repeat%                                                                                                                                                     
}
\newcounter{staves}
\newcommand{\musicstaves}[1]{%                                                                                                                                 
  \setcounter{staves}{0}                                                                                                                                       
  \loop                                                                                                                                                        
    \musicstaff\par\vspace*{5\baselineskip} % doesn't work                                                                                                                
    \stepcounter{staves}                                                                                                                                       
    \ifnum\value{staves} < #1                                                                                                                                  
  \repeat%                                                                                                                                                     
}

\begin{document}

\musicstaves{3} % only prints one staff

\end{document}

4 Answers 4

8

\loop is a very simple macro and can not be nested at the same group level:

\documentclass{article}

\newcounter{stafflines}
\newcommand{\musicstaff}{%                                                                                                                                     
  \setcounter{stafflines}{0}                                                                                                                                   
  \noindent                                                                                                                                                    
  \loop                                                                                                                                                        
    \rule{\linewidth}{0.4pt}%                                                                                                                                  
    \vspace*{-0.5\baselineskip}                                                                                                                                
    \stepcounter{stafflines}                                                                                                                                   
    \ifnum\value{stafflines} < 5                                                                                                                               
  \repeat%                                                                                                                                                     
}
\newcounter{staves}
\newcommand{\musicstaves}[1]{%                                                                                                                                 
  \setcounter{staves}{0}                                                                                                                                       
  \loop                                                                                                                                                        
    {\musicstaff\par\vspace*{5\baselineskip}} % does work                                                                                                                
    \stepcounter{staves}                                                                                                                                       
    \ifnum\value{staves} < #1                                                                                                                                  
  \repeat%                                                                                                                                                     
}

\begin{document}

\musicstaves{3} % only prints one staff

\end{document}

or again with white space fixed so you do not get underfill hbox warnings and spurious spaces

\documentclass{article}

\newcounter{stafflines}
\newcommand{\musicstaff}{%
  \setcounter{stafflines}{0}%
  \noindent
  \loop
    \rule{\linewidth}{0.4pt}%
    \vspace*{-0.5\baselineskip}
    \stepcounter{stafflines}%
    \ifnum\value{stafflines} < 5
  \repeat
}
\newcounter{staves}
\newcommand{\musicstaves}[1]{%
  \setcounter{staves}{0}%
  \loop
    {\musicstaff\par\vspace*{5\baselineskip}}% does work
    \stepcounter{staves}%
    \ifnum\value{staves} < #1
  \repeat
}

\begin{document}

\musicstaves{3} % only prints one staff

\end{document}
3
  • Thanks! I guess I thought wrapping the loop in a macro contained its scope or something (probably not the right term). PS - Is there a better way than this to make the lines close together? Sep 8, 2015 at 19:05
  • @AndrewCashner I posted again anyway with you % at ends of line fixed, to get rid of the underfull box warnings, that prints them closer together how close do you want? Sep 8, 2015 at 19:07
  • Thanks - I just meant, is there a better way than the negative \vspace to make them this close together? Sep 8, 2015 at 19:33
3

I'm a fan of \foreach from pgffor.sty, so...

\documentclass{article}

\usepackage{pgffor}

\newcommand{\staff}[2][5]{%[# of lines per staff]{# of staves}
    \begingroup
    \parindent0pt
    \noindent
    \foreach \y in {1,...,#2}{%
        \foreach \x in {1,2,...,#1}{%
            \rule{\textwidth}{0.4pt}% {<staff width>}{<line thickness>}
            \par
            \nointerlineskip
            \vskip6pt% Distance between the lines
        }%
        \vspace{0.25in}% Distance between staves
    }%
    \endgroup
}


\begin{document}

\staff{5}

\vspace{0.5in}

\staff[4]{3}

\end{document}

By default, this draws a 5-line staff, but you can easily change that with optional argument to \staff. I have altered my original answer to allow for specifying the number of staves to be drawn.

enter image description here

3
  • I like the way you do the line spacing, and the optional argument for different numbers of lines. You don't have the second loop for producing multiple staves, though. Sep 8, 2015 at 19:49
  • You're quite right, I should have spotted that. I have emended my answer accordingly.
    – sgmoye
    Sep 8, 2015 at 21:55
  • I altered my answer slightly: I use TikZ for a great many things, so it is second nature for me to write \usepackage{tikz}. In this case, however, it is not necessary to invoke all of TikZ to use \foreach as pgffor.sty by itself will provide what is required.
    – sgmoye
    Sep 9, 2015 at 10:36
2

An expl3 customizable version:

\documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn

\keys_define:nn { cashner/staff }
 {
  width  .dim_set:N = \l_cashner_staff_width_dim,
  number .int_set:N = \l_cashner_staff_number_int,
  gap    .dim_set:N = \l_cashner_staff_gap_dim,
  width  .initial:n = \linewidth,
  number .initial:n = 1,
  gap    .initial:n = 5\baselineskip,
 }

\NewDocumentCommand{\musicstaves}{O{number=1}}
 {
  \par
  \group_begin:
  \keys_set:nn { cashner/staff } { #1 }
  \cashner_staves:
  \group_end:
 }

\cs_new_protected:Nn \cashner_staves:
 {
  \cashner_staff:
  \prg_replicate:nn { \l_cashner_staff_number_int - 1 }
   {
    \vspace{ \l_cashner_staff_gap_dim }
    \cashner_staff:
   }
 }

\cs_new_protected:Nn \cashner_staff:
 {
  \hrule width \l_cashner_staff_width_dim
  \prg_replicate:nn { 4 }
   {
    \vspace{4pt}
    \hrule width \l_cashner_staff_width_dim
   }
 }
\ExplSyntaxOff


\begin{document}

\musicstaves[number=3]

\bigskip

\musicstaves[width=3cm] % only one

\bigskip

\musicstaves[number=2,gap=1cm,width=5cm]

\end{document}

enter image description here

A variant with a “fill” feature; besides fill you can specify all other keys (of course setting number has no effect).

\documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn

\keys_define:nn { cashner/staff }
 {
  fill   .bool_set:N = \l_cashner_staff_fill_bool,
  width  .dim_set:N  = \l_cashner_staff_width_dim,
  number .int_set:N  = \l_cashner_staff_number_int,
  gap    .dim_set:N  = \l_cashner_staff_gap_dim,
  fill   .initial:n  = false,
  fill   .default:n  = true,
  width  .initial:n  = \linewidth,
  number .initial:n  = 1,
  gap    .initial:n  = 5\baselineskip,
 }

\box_new:N \l_cashner_staff_box

\NewDocumentCommand{\musicstaves}{O{number=1}}
 {
  \par
  \group_begin:
  \keys_set:nn { cashner/staff } { #1 }
  \bool_if:NTF \l_cashner_staff_fill_bool
   { \cashner_staves_fill: }
   { \cashner_staves: }
  \group_end:
 }

\cs_new_protected:Nn \cashner_staves:
 {
  \cashner_staff:
  \prg_replicate:nn { \l_cashner_staff_number_int - 1 }
   {
    \vspace{ \l_cashner_staff_gap_dim }
    \cashner_staff:
   }
 }

\cs_new_protected:Nn \cashner_staff:
 {
  \hrule width \l_cashner_staff_width_dim
  \prg_replicate:nn { 4 }
   {
    \vspace{4pt}
    \hrule width \l_cashner_staff_width_dim
   }
 }

\cs_new_protected:Nn \cashner_staves_fill:
 {
  \cashner_staff:
  \vbox_set:Nn \l_cashner_staff_box
   {
    \vspace{ \l_cashner_staff_gap_dim }
    \cashner_staff:
   }
  \xleaders \box_use:N \l_cashner_staff_box \vfill
  \vspace*{0pt}
 }

\ExplSyntaxOff


\begin{document}

\musicstaves[number=3]

\bigskip

\musicstaves[width=3cm] % only one

\bigskip

\musicstaves[number=2,gap=1cm,width=5cm]

\clearpage

\musicstaves[fill]

\end{document}
1

I took something from every answer to make a simple package that meets my needs. You can draw any number staves with any number of lines each, and you can control the spacing both between and inside the staves. The package is below, followed by a sample document demonstrating different uses.


Package musicstaves.sty

\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{musicstaves}[2015/09/08 Draw blank music staff lines]

% Adjustable spacing lengths
% Space between the lines inside the staff
\newlength{\insidestaffspace}
\setlength{\insidestaffspace}{4pt}
% Can adjust spacing between staves
\newlength{\betweenstaffspace}
\setlength{\betweenstaffspace}{1cm}

%% Draw a single staff with a specified number of lines per staff
\newcounter{stafflines}
% #1 lines per staff
\newcommand{\musicstaff}[1]{%
  \setcounter{stafflines}{0}%
  \noindent
    \loop
    \vspace{\insidestaffspace}%
    \hrule width\linewidth \nobreak
   \stepcounter{stafflines}%
   \ifnum\value{stafflines} < #1
   \repeat%
}

%% Draw multiple staves
\newcounter{staves}
% #1 lines per staff (optional, defaults to 5)
% #2 total staves to print
\newcommand{\musicstaves}[2][5]{%
  \setcounter{staves}{0}%
  \loop
    {\musicstaff{#1}\par\vspace*{\betweenstaffspace}}
    \stepcounter{staves}%
    \ifnum\value{staves} < #2
  \repeat%
}

%% Set lengths for widely spaced staves (inside staff and between staves)
\newcommand{\widestaves}{%
  \setlength{\insidestaffspace}{2\insidestaffspace}
  \setlength{\betweenstaffspace}{2\betweenstaffspace}
}

\endinput

Sample document mwe.tex

\documentclass{article}
\usepackage{musicstaves}

\begin{document}

\section*{4-line staff}

\musicstaff{4}

\section*{5-line default staves}

\musicstaves{4}

\section*{6-line staves}

\musicstaves[6]{2}

\section*{Default staves with increased spacing}

{\setlength{\betweenstaffspace}{1.5\betweenstaffspace}
\musicstaves{3}
}

\section*{Wide staves}

{\setwidestaves \musicstaves{4} }

\end{document}

enter image description here enter image description here

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .