8

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}
7

\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}
  • 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? – musarithmia Sep 8 '15 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? – David Carlisle Sep 8 '15 at 19:07
  • Thanks - I just meant, is there a better way than the negative \vspace to make them this close together? – musarithmia Sep 8 '15 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

  • 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. – musarithmia Sep 8 '15 at 19:49
  • You're quite right, I should have spotted that. I have emended my answer accordingly. – sgmoye Sep 8 '15 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 '15 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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.