texlive[64966] Master: songproj (7nov22)

commits+karl at tug.org commits+karl at tug.org
Mon Nov 7 21:29:26 CET 2022


Revision: 64966
          http://tug.org/svn/texlive?view=revision&revision=64966
Author:   karl
Date:     2022-11-07 21:29:26 +0100 (Mon, 07 Nov 2022)
Log Message:
-----------
songproj (7nov22)

Modified Paths:
--------------
    trunk/Master/tlpkg/bin/tlpkg-ctan-check
    trunk/Master/tlpkg/tlpsrc/collection-music.tlpsrc

Added Paths:
-----------
    trunk/Master/texmf-dist/doc/latex/songproj/
    trunk/Master/texmf-dist/doc/latex/songproj/LICENSE
    trunk/Master/texmf-dist/doc/latex/songproj/README.md
    trunk/Master/texmf-dist/doc/latex/songproj/examples/
    trunk/Master/texmf-dist/doc/latex/songproj/examples/clementine.txt
    trunk/Master/texmf-dist/doc/latex/songproj/song2tex.py
    trunk/Master/texmf-dist/doc/latex/songproj/songproj.pdf
    trunk/Master/texmf-dist/source/latex/songproj/
    trunk/Master/texmf-dist/source/latex/songproj/Makefile
    trunk/Master/texmf-dist/source/latex/songproj/songproj.dtx
    trunk/Master/texmf-dist/source/latex/songproj/songproj.ins
    trunk/Master/texmf-dist/tex/latex/songproj/
    trunk/Master/texmf-dist/tex/latex/songproj/songproj.sty
    trunk/Master/tlpkg/tlpsrc/songproj.tlpsrc

Added: trunk/Master/texmf-dist/doc/latex/songproj/LICENSE
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songproj/LICENSE	                        (rev 0)
+++ trunk/Master/texmf-dist/doc/latex/songproj/LICENSE	2022-11-07 20:29:26 UTC (rev 64966)
@@ -0,0 +1,24 @@
+Copyright (C) 2022 by Tanguy Ortolo <tanguy+latex at ortolo.eu>
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+3. Neither the name of the copyright holder nor the names of its
+   contributors may be used to endorse or promote products derived from this
+   software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.

Added: trunk/Master/texmf-dist/doc/latex/songproj/README.md
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songproj/README.md	                        (rev 0)
+++ trunk/Master/texmf-dist/doc/latex/songproj/README.md	2022-11-07 20:29:26 UTC (rev 64966)
@@ -0,0 +1,66 @@
+songproj
+========
+
+Commands and environments for generating Beamer slideshows with song lyrics,
+typically for religious services with a projector.
+
+Author: Tanguy Ortolo
+License: 3-Clause BSD
+
+Description
+-----------
+
+This package, together with the Beamer class, is used to generate slideshows
+with song lyrics. This is typically used in religious services in churches
+equipped with a projector, for which this package has been written, but it can
+be useful for any type of singing assembly. It provides environments to
+describe a song in a natural way, and formatting it into slides with overlays.
+
+Example
+-------
+
+Detailed usage can be found in the PDF documentation. Here is a simple example:
+
+```latex
+\documentclass{beamer}
+\usepackage{songproj}
+\begin{document}
+  \begin{frame}
+    \begin{song}{2}
+      \longest{Light she was, and like a fairy,}
+      \begin{couplet}
+        In a cavern, in a canyon, \\
+        Excavating for a mine. \\
+        Dwelt a miner, forty-niner, \\
+        And his daughter, Clementine. \\
+      \end{couplet}
+      \begin{refrain}
+        Oh my darling, oh my darling, \\
+        Oh my darling Clementine, \\
+        You are lost and gone forever, \\
+        Dreadful sorry, Clementine. \\
+      \end{refrain}
+      \begin{couplet}
+        Light she was, and like a fairy, \\
+        And her shoes were number nine, \\
+        Herring boxes, without topses, \\
+        Sandals were for Clementine. \\
+      \end{couplet}
+      \begin{couplet}
+        […]
+      \end{couplet}
+    \end{song}
+  \end{frame}
+\end{document}
+```
+
+Add-on script
+-------------
+
+This package comes with an additional script, `song2tex.py`, that can be used
+to convert plain-text song lyrics to the expected LaTeX markup. The file
+`example/clementine.txt` can be used to test it:
+
+```sh
+./song2tex.py examples/clementine.txt /tmp/clementine.tex
+```


Property changes on: trunk/Master/texmf-dist/doc/latex/songproj/README.md
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/doc/latex/songproj/examples/clementine.txt
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songproj/examples/clementine.txt	                        (rev 0)
+++ trunk/Master/texmf-dist/doc/latex/songproj/examples/clementine.txt	2022-11-07 20:29:26 UTC (rev 64966)
@@ -0,0 +1,39 @@
+1. In a cavern, in a canyon,
+Excavating for a mine.
+Dwelt a miner, forty-niner,
+And his daughter, Clementine.
+
+C. Oh my darling, oh my darling,
+Oh my darling Clementine,
+You are lost and gone forever,
+Dreadful sorry, Clementine.
+
+2. Light she was, and like a fairy,
+And her shoes were number nine,
+Herring boxes, without topses,
+Sandals were for Clementine.
+
+3. Drove she ducklings to the water,
+Ev'ry morning just at nine,
+Hit her foot against a splinter
+Fell into the foaming brine.
+
+4. Ruby lips above the water,
+Blowing bubbles, soft and fine,
+Alas, for me! I was no swimmer,
+So I lost my Clementine.
+
+5. In a churchyard, near the canyon,
+Where the myrtle boughs entwine,
+There grow roses, and other posies,
+Fertilized by Clementine.
+
+6. Then the miner, forty-niner,
+Soon began to peak and pine,
+Thought he "oughter jine" his daughter,
+Now he's with his Clementine.
+
+7. In my dreams she still doth haunt me,
+Robed in garments soaked in brine;
+Though in life I used to hug her,
+Now she's dead, I'll draw the line.


Property changes on: trunk/Master/texmf-dist/doc/latex/songproj/examples/clementine.txt
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/doc/latex/songproj/song2tex.py
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songproj/song2tex.py	                        (rev 0)
+++ trunk/Master/texmf-dist/doc/latex/songproj/song2tex.py	2022-11-07 20:29:26 UTC (rev 64966)
@@ -0,0 +1,101 @@
+#!/usr/bin/env python3
+
+import argparse
+import enum
+import re
+
+
+# The following regexp match the typical markers of refrain and couplet:
+re_refrain = re.compile(r'(R(efrain)?|C(horus)?)( |[-./] ?)', re.IGNORECASE)
+re_couplet = re.compile(r'\d+( |[-./] ?)')
+
+
+def match_rest(regexp, s):
+    """Match the start of string s against the given regexp:
+    * if it matches, return the non-matching part;
+    * it it does not matches, return None."""
+    if (m := regexp.match(s)) is not None:
+        return s[m.end():]
+    else:
+        return None
+
+
+class State(enum.Enum):
+    """A state indicates in what context we are while parsing a song."""
+    LIMBO   = 0 # Not in the refrain or in a couplet
+    REFRAIN = 1 # In the refrain
+    COUPLET = 2 # In a couplet
+
+
+def parse_song(lines, re_refrain, re_couplet):
+    # At the beginning of the song, we are not in the refrain or in a couplet.
+    state = State.LIMBO
+    # This variable will be used to record the longest line seen so far.
+    longest = ''
+    for line in lines:
+        line = line.rstrip()
+        if state == State.LIMBO:
+            # We are not in the refrain or in a couplet. Depending on what we find:
+            if (rest := match_rest(re_refrain, line)) is not None:
+                # when a refrain start marker is found, enter the refrain.
+                yield '\\begin{refrain}\n'
+                state = State.REFRAIN
+                line = rest
+                yield '  {} \\\\\n'.format(line)
+            elif (rest := match_rest(re_couplet, line)) is not None:
+                # when a couplet start marker is found, enter a couplet.
+                yield '\\begin{couplet}\n'
+                state = State.COUPLET
+                line = rest
+                yield '  {} \\\\\n'.format(line)
+            elif line != '':
+                # when a non-empty line is found, also enter a couplet as all.
+                yield '\\begin{couplet}\n'
+                state = State.COUPLET
+                yield '  {} \\\\\n'.format(line)
+        elif state == State.REFRAIN:
+            # We are in the refrain. Depending on what we find:
+            if line != '':
+                # when a non-empty line is found, stay in the refrain.
+                yield '  {} \\\\\n'.format(line)
+            else:
+                # when an empty line is found, leave the refrain.
+                yield '\\end{refrain}\n\n'
+                state = State.LIMBO
+        elif state == State.COUPLET:
+            # We are in a couplet. Depending on what we find:
+            if line != '':
+                # when a non-empty line is found, stay in the couplet.
+                yield '  {} \\\\\n'.format(line)
+            else:
+                # when an empty line is found, leave the couplet.
+                yield '\\end{couplet}\n\n'
+                state = State.LIMBO
+        # No matter what we found, we have a song line, possibly empty, that we
+        # have to check to see if it could not be the new longest line seen so far.
+        if len(line) > len(longest):
+            longest = line
+    # The song text file is now finished. We may still be in the refrain or in a
+    # couplet, that we have to close.
+    if state == State.REFRAIN:
+        yield '\\end{refrain}\n\n'
+    elif state == State.COUPLET:
+        yield '\\end{couplet}\n\n'
+    # Now print the longest line of the entire song.
+    yield '\\longest{{{}}}\n'.format(longest)
+
+
+def main(args=None):
+    parser = argparse.ArgumentParser(description="Convert song text to LaTeX songproj markup")
+    parser.add_argument("--refrain", '-r', type=re.compile, default=re_refrain, metavar="REGEXP",
+            help="specify the refrain marker (regexp, default to \"{}\")".format(re_refrain.pattern))
+    parser.add_argument("--couplet", '-c', type=re.compile, default=re_couplet, metavar="REGEXP",
+            help="specify the couplet marker (regexp, default \"{}\")".format(re_couplet.pattern))
+    parser.add_argument("infile", help="input song text file", type=argparse.FileType('r'))
+    parser.add_argument("outfile", help="output LaTeX file", type=argparse.FileType('w'))
+    args = parser.parse_args(args)
+    for line in parse_song(args.infile, args.refrain, args.couplet):
+        args.outfile.write(line)
+
+if __name__ == "__main__":
+    main()


Property changes on: trunk/Master/texmf-dist/doc/latex/songproj/song2tex.py
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Added: trunk/Master/texmf-dist/doc/latex/songproj/songproj.pdf
===================================================================
(Binary files differ)

Index: trunk/Master/texmf-dist/doc/latex/songproj/songproj.pdf
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songproj/songproj.pdf	2022-11-07 20:28:16 UTC (rev 64965)
+++ trunk/Master/texmf-dist/doc/latex/songproj/songproj.pdf	2022-11-07 20:29:26 UTC (rev 64966)

Property changes on: trunk/Master/texmf-dist/doc/latex/songproj/songproj.pdf
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/pdf
\ No newline at end of property
Added: trunk/Master/texmf-dist/source/latex/songproj/Makefile
===================================================================
--- trunk/Master/texmf-dist/source/latex/songproj/Makefile	                        (rev 0)
+++ trunk/Master/texmf-dist/source/latex/songproj/Makefile	2022-11-07 20:29:26 UTC (rev 64966)
@@ -0,0 +1,31 @@
+all: sty pdf
+
+sty: songproj.sty
+
+pdf: songproj.pdf
+
+%.pdf: %.dtx %.gls %.ind
+	xelatex $<
+
+%.glo %.aux %.idx: %.dtx
+	xelatex $<
+
+%.gls: %.glo
+	makeindex -s gglo.ist -o $@ $<
+
+%.ind: %.idx
+	makeindex -s gind.ist -o $@ $<
+
+%.sty: %.ins %.dtx
+	xelatex $<
+
+mostlyclean:
+	rm -f *.aux *.glo *.gls *.hd *.idx *.ilg *.ind *.log *.out
+
+clean: mostlyclean
+	rm -f *.pdf
+
+maintainer-clean: clean
+	rm -f *.sty
+
+.PHONY: all sty doc mostlyclean clean maintainer-clean


Property changes on: trunk/Master/texmf-dist/source/latex/songproj/Makefile
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/source/latex/songproj/songproj.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/songproj/songproj.dtx	                        (rev 0)
+++ trunk/Master/texmf-dist/source/latex/songproj/songproj.dtx	2022-11-07 20:29:26 UTC (rev 64966)
@@ -0,0 +1,1024 @@
+% \iffalse meta-comment
+%
+% Copyright (C) 2022 by Tanguy Ortolo <tanguy+latex at ortolo.eu>
+% 
+% Redistribution and use in source and binary forms, with or without
+% modification, are permitted provided that the following conditions are met:
+% 1. Redistributions of source code must retain the above copyright notice,
+%    this list of conditions and the following disclaimer.
+% 2. Redistributions in binary form must reproduce the above copyright notice,
+%    this list of conditions and the following disclaimer in the documentation
+%    and/or other materials provided with the distribution.
+% 3. Neither the name of the copyright holder nor the names of its
+%    contributors may be used to endorse or promote products derived from this
+%    software without specific prior written permission.
+% 
+% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+% POSSIBILITY OF SUCH DAMAGE.
+%
+% \fi
+%
+% \iffalse
+%<*driver>
+\ProvidesFile{songproj.dtx}
+%</driver>
+%<package>\NeedsTeXFormat{LaTeX2e}[2020/10/01]
+%<package>\ProvidesPackage{songproj}
+%<*package>
+    [2022/11/07 v1.0.1 Song projection]
+%</package>
+%
+%<*driver>
+\documentclass{l3doc}
+\usepackage{fontspec}
+\usepackage{bookmark}
+\EnableCrossrefs
+\CodelineIndex
+\RecordChanges
+\begin{document}
+  \DocInput{songproj.dtx}
+  \PrintChanges
+  \PrintIndex
+\end{document}
+%</driver>
+% \fi
+%
+% \CheckSum{256}
+%
+% \CharacterTable
+%  {Upper-case    \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
+%   Lower-case    \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z
+%   Digits        \0\1\2\3\4\5\6\7\8\9
+%   Exclamation   \!     Double quote  \"     Hash (number) \#
+%   Dollar        \$     Percent       \%     Ampersand     \&
+%   Acute accent  \'     Left paren    \(     Right paren   \)
+%   Asterisk      \*     Plus          \+     Comma         \,
+%   Minus         \-     Point         \.     Solidus       \/
+%   Colon         \:     Semicolon     \;     Less than     \<
+%   Equals        \=     Greater than  \>     Question mark \?
+%   Commercial at \@     Left bracket  \[     Backslash     \\
+%   Right bracket \]     Circumflex    \^     Underscore    \_
+%   Grave accent  \`     Left brace    \{     Vertical bar  \|
+%   Right brace   \}     Tilde         \~}
+%
+%
+% \changes{v0.1.0}{2021/09/19}{Initial version}
+% \changes{v0.3.0}{2021/09/26}{Use \textsf{expl3} naming conventions}
+% \changes{v1.0.0}{2021/11/07}{First public release}
+% \changes{v1.0.1}{2021/11/07}{Add the generated extension file to Git}
+%
+% \GetFileInfo{songproj.dtx}
+%
+% \DoNotIndex{\newcommand,\newenvironment}
+% \DoNotIndex{\begin,\end}
+% \DoNotIndex{\bool,\bool_gset_false:N,\bool_gset_true:N,\bool_if:NTF,\bool_new:N}
+% \DoNotIndex{\cs_gset:Npn,\cs_new:Npn}
+% \DoNotIndex{\dim,\dim_compare:nNnTF,\dim_new:N,\dim_zero:N}
+% \DoNotIndex{\int,\int_compare:nNnTF,\int_gset_eq:NN,\int_new:N,\int_step_function:nN,\int_eval:n,\int_mod:nn,\int_step_inline:nn,\int_use:N}
+% \DoNotIndex{\seq,\seq_count:N,\seq_gclear:N,\seq_gput_right:Nn,\seq_gset_from_clist:Nn,\seq_if_empty:NTF,\seq_item:Nn,\seq_map_function:NN,\seq_new:N}
+% \DoNotIndex{\tl,\tl_gclear:N,\tl_gset:Nn,\tl_new:N,\tl_use:N,\tl_if_empty:NTF}
+% \DoNotIndex{\IfNoValueTF,\NewDocumentCommand,\NewDocumentEnvironment}
+% \DoNotIndex{\env,\cmd,\textsf,\vfill}
+%
+%
+% \title{The \textsf{songproj} package\thanks{This document
+%   corresponds to \textsf{songproj}~\fileversion, dated \filedate.}}
+% \author{Tanguy Ortolo \\ \texttt{tanguy+latex at ortolo.eu}}
+%
+% \maketitle
+% \section{Introduction}
+%
+% This package, together with the \cls{beamer} class, is used to generate
+% slideshows with song lyrics. This is typically used in religious services in
+% churches equipped with a projector, for which this package has been written,
+% but it can be useful for any type of singing assembly\footnote{Indeed, the
+% song used here as an example is not really a religious one! It was chosen
+% because it is in the public domain and the author likes it.}. It provides
+% environments to describe a song in a natural way, and formatting it into
+% slides with overlays.
+%
+% \section{Usage}
+%
+% \subsection{The \env{song} environment}
+%
+% The main feature of this package is the \env{song}, that allows the user to
+% describe an entire song that will be formatted into slides.
+%
+% \paragraph{}
+% \DescribeEnv{song}
+% The \env{song}\marg{stanzas per slide}\oarg{couplet list} environment is used
+% around an entire song. It takes a mandatory argument, \meta{stanzas per
+% slide}, to specify whether the user wants to show one or two
+% stanzas\footnote{including the refrain} on the slide. An optional argument,
+% \meta{couplet list} is a comma-separated list of couplet (or verse) indexes,
+% that allows the user to indicate that they want to include only these
+% couplets of a large song: without this, all couplets will be included.
+%
+% Inside of the \env{song} environment, the user will use the \cs{longest}
+% command and the \env{intro}, \env{refrain}, \env{couplet}\footnote{We
+% chose to use the French words \emph{refrain} and \emph{couplet} for several
+% reason: the author is French, these words are understandable in English and
+% their English equivalents, \emph{chorus} and \emph{verse}, have multiple
+% meanings that would make them very ambiguous in both usage and implementation
+% of this package.} and \env{final} environments.
+%
+% \paragraph{Warning} Inside a \env{song} environment, it is an error to write
+% anything that is not an \env{intro}, \env{refrain}, \env{couplet},
+% \env{final} environment or a \cs{longest} command. Direct text would be
+% typeset in a way that would disrupt the song formatting.
+%
+% \paragraph{}
+% \begin{function}{\longest}
+% \begin{syntax}
+% \cs{longest}\marg{song line}
+% \end{syntax}
+% Inside a \env{song} environment, the \cs{longest}\marg{song line} command is
+% used to declare the longest line of a song, that will be used to properly
+% center the song stanzas, as allowed by the \pkg{verse} package. That line
+% is only used to compute and record its length, and will not be typeset.
+% \end{function}
+%
+% \changes{v0.4.0}{2021/09/29}{Document \env{intro} and \env{final} environments}
+%
+% \paragraph{}
+% \DescribeEnv{intro}
+% Inside a \env{song} environment, the optional \env{intro} environment
+% declares a number of lines meant to be sung once, at the beginning of the
+% song. In a psalm, this may be an antiphon.
+%
+% \paragraph{}
+% \DescribeEnv{refrain}
+% Inside a \env{song} environment, the optional \env{refrain} environment
+% declares the song refrain (or chorus). A song may start with its refrain, or
+% with a first couplet, followed by the refrain. It is not useful to declare
+% the refrain several time, as the \env{song} environment will take care of
+% repeating between the couplets.
+%
+% \paragraph{}
+% \DescribeEnv{couplet}
+% Inside a \env{song} environment, the \env{couplet} environment declares each
+% couplet (or verse) of the song.
+%
+% \paragraph{}
+% \DescribeEnv{final}
+% Inside a \env{song} environment, the optional \env{final} environment
+% declares a number of lines meant to be sung once, at the end of the song. In
+% an hymn, that may be a doxology.
+%
+% \paragraph{Example} \label{example/song}
+% The following song is defined with three couplets and a refrain. Since its
+% begins with a couplet, it will be formatted with the first couplet, the
+% refrain, the second couplet, the refrain, and so on.
+%
+% The \env{song} environment is given two arguments, |{2}[1,2]|. The first one
+% tells it to show two stanzas, that is, both a couplet and the refrain, on the
+% generated slide. The second argument tells it to include only the first two
+% couplets in the output.
+%
+% \begin{verbatim}
+% \begin{frame}
+%   \begin{song}{2}[1,2]
+%     \longest{Light she was, and like a fairy,}
+%     \begin{couplet}
+%       In a cavern, in a canyon, \\
+%       Excavating for a mine. \\
+%       Dwelt a miner, forty-niner, \\
+%       And his daughter, Clementine. \\
+%     \end{couplet}
+%     \begin{refrain}
+%       Oh my darling, oh my darling, \\
+%       Oh my darling Clementine, \\
+%       You are lost and gone forever, \\
+%       Dreadful sorry, Clementine. \\
+%     \end{refrain}
+%     \begin{couplet}
+%       Light she was, and like a fairy, \\
+%       And her shoes were number nine, \\
+%       Herring boxes, without topses, \\
+%       Sandals were for Clementine. \\
+%     \end{couplet}
+%     \begin{couplet}
+%       […]
+%     \end{couplet}
+%   \end{song}
+% \end{frame}
+% \end{verbatim}
+%
+% \subsection{The \cs{inputsong} command}
+% \changes{v1.0.0}{2021/11/07}{Document the \cs{inputsong} command}
+%
+% \begin{function}{\inputsong}
+% \begin{syntax}
+% \cs{inputsong}\marg{file}\marg{stanzas per slide}\oarg{couplet list}
+% \end{syntax}
+%
+% The \cs{inputsong} command environment is used as a shortcut for
+% typesetting a song written in an external file. That file should contain the
+% song content, including intro, refrain, couplet or final as needed, but
+% \emph{without} being wrapped in a \env{song} environment.
+%
+% For instance, one could write a file named \file{clementine.tex} containing
+% the \emph{content} of the \env{song} environment shown in example
+% page~\pageref{example/song}, and use it in a slideshow:
+%
+% \begin{verbatim}
+% \frame{\inputsong{clementine.tex}{2}[1,2]}
+% \end{verbatim}
+% \end{function}
+%
+% \subsection{The \env{refrain}, \env{couplet}, \env{intro} and \env{final} environments}
+%
+% \changes{v0.4.0}{2021/09/29}{Document \env{intro} and \env{final} environments}
+%
+% These commands are also usable outside of a \env{song} environment. In that
+% case, they simply format a refrain or couplet, which can be useful when you
+% need more manual control.
+%
+% \paragraph{}
+% \DescribeEnv{refrain}
+% Outside of a \env{song} environment, this environment simply wraps its
+% content inside a \env{structure} and a \env{verse} environment. It takes an
+% optional \meta{verse width} argument, that is used to properly center the
+% refrain, as allowed by the \pkg{verse} package.
+%
+% \paragraph{}
+% \DescribeEnv{couplet}
+% Outside of a \env{song} environment, this environment simply wraps its
+% content inside a \env{verse} environment. It takes an optional \meta{verse
+% width} argument, that is used to properly center the refrain, as allowed by
+% the \pkg{verse} package.
+%
+% \paragraph{}
+% \DescribeEnv{intro}
+% \DescribeEnv{final}
+% Outside of a \env{song} environment, these environments simply wrap their
+% content inside a \env{em} and a \env{verse} environment. They takes an
+% optional \meta{verse width} argument, that is used to properly center the
+% refrain, as allowed by the \pkg{verse} package.
+%
+% \subsection{Usage tips}
+%
+% For regular offices, there are several suggestions that can ease the creation
+% and usage of lyric slideshows.
+%
+% \subsubsection{Using dedicated song files}
+%
+% It is suggested to write song lyrics in dedicated files, each containing a
+% single the \emph{content} of a \env{song} environment, without the
+% environment wrapping itself. They can then be used with the \cs{inputsong}
+% command.
+%
+% For instance, one could write a file named \file{clementine.tex} containing
+% the \emph{content} of the \env{song} environment shown in example
+% page~\pageref{example/song}. It would then be used in a slideshow such as:
+%
+% \begin{verbatim}
+% \documentclass{beamer}
+% \usepackage{songproj}
+%
+% \begin{document}
+%   \begin{frame}
+%     \inputsong{clementine.tex}{2}[1,2,3]
+%   \end{frame}
+% \end{document}
+% \end{verbatim}
+%
+% \subsubsection{Importing text lyrics}
+%
+% Song lyrics are often found in text format with basic markup:
+%
+% \begin{verbatim}
+% 1. In a cavern, in a canon,
+% Excavating for a mine.
+% Dwelt a miner, forty-niner,
+% And his daughter, Clementine.
+%
+% C. Oh my darling, oh my darling,
+% Oh my darling Clementine
+% You are lost and gone forever,
+% Dreadful sorry Clementine.
+%
+% 2. Light she was, and like a fairy,
+% And her shoes were number nine,
+% Herring boxes, without topses,
+% Sandals were for Clementine.
+%
+% […]
+% \end{verbatim}
+%
+% To avoid the tedious task of manually removing text and adding \LaTeX{}
+% markup, we provide the \file{song2tex.py} helper. Please refer to its
+% embedded help for detailed instructions about its usage:
+%
+% \begin{verbatim}
+% $ ./song2tex.py --help
+% $ ./song2tex.py clementine.txt clementine.tex
+% \end{verbatim}
+%
+% \subsubsection{Projection layout advice}
+% \changes{v1.0.0}{2021/11/07}{Add projection layout advice}
+%
+% During a religious service, a song lyrics projection is only a support, and
+% should not draw their attention away from the main feature, which is the
+% common prayer.
+%
+% I therefore suggest using a very simple Beamer theme, such as its default one
+% with the \href{https://github.com/rchurchley/beamercolortheme-owl}{owl} color
+% theme, and removing the navigation symbols. I also suggest not showing song
+% titles (or anything else that is not actually sung by the assembly) unless
+% there is a good reason to do so, such as getting used to a song or set of
+% songs you intend to reuse often.
+%
+% \begin{verbatim}
+% \documentclass{beamer}
+% \usecolortheme{owl}
+% \setbeamertemplate{navigation symbols}{}
+% \usepackage{songproj}
+% \begin{document}
+%   […]
+% \end{document}
+% \end{verbatim}
+%
+% \subsubsection{Projection advice}
+% \changes{v1.0.0}{2021/11/07}{Add projection advice}
+%
+% For projecting song lyrics, you can take advantage of using a PDF
+% presentation software able to show a presenter console on your laptop screen,
+% and the current slide on the projector. Software like as
+% \href{https://pdfpc.github.io/}{pdfpc} or
+% \href{https://github.com/Cimbali/pympress/}{Pympress} can also understand and
+% adapt their display to the concept of Beamer overlay.
+%
+% \StopEventually{}
+%
+% \section{Implementation}
+%
+% \subsection{Dependencies}
+%
+% This module is written using \LaTeX3 programming interfaces and command
+% definitions:
+%
+%    \begin{macrocode}
+\RequirePackage{expl3}
+\RequirePackage{xparse}
+%    \end{macrocode}
+%
+% The implementation of the \env{song} environment and its friends is mainly
+% based on the \textsf{verse} package:
+%
+%    \begin{macrocode}
+\RequirePackage{verse}
+%    \end{macrocode}
+%
+% \subsection{Internal definitions}
+%
+% Almost all definitions use the \textsf{expl3} syntax:
+%
+%    \begin{macrocode}
+\ExplSyntaxOn
+%    \end{macrocode}
+%
+% \subsubsection{Internal variables}
+%
+% \changes{v0.2.0}{2021/09/23}{Use \texttt{sp} prefix for functions and
+%   variables}
+%
+% We define a number of internal variables, that are used when reading and
+% formatting a song. All of these variables are meant to be set globally: since
+% there is no notion of a song within a song, we are certain that we will
+% always be either outside of a song or inside a single song.
+%
+%    \begin{macrocode}
+\bool_new:N \g__sp_song_bool             % are we in a song?
+\bool_new:N \g__sp_song_start_bool       % are we at the start of a song?
+\bool_new:N \g__sp_refrain_first_bool    % does current song start with the
+                                         % refrain?
+\int_new:N  \g__sp_stanzas_per_slide_int % number of stanzas to show on each
+                                         % slide (1 or 2)
+\dim_new:N  \g__sp_linewidth_dim         % length of the longest line in current
+                                         % song
+\tl_new:N   \g__sp_intro_tl              % current song intro
+\tl_new:N   \g__sp_refrain_tl            % current song refrain
+\seq_new:N  \g__sp_couplets_seq          % current song couplets
+\tl_new:N   \g__sp_final_tl              % current song final
+\seq_new:N  \g__sp_couplet_indexes_seq   % indexes of couplets to include
+%    \end{macrocode}
+%
+% \subsubsection{Internal environments}
+%
+% These are high-level internal environments, that are used in the
+% implementation of user interface environments.
+%
+% \begin{environment}{__sp_refrain}
+% This environment simply formats a song refrain. It is used in the user interface
+% \env{refrain} environment.
+%
+%    \begin{macrocode}
+\NewDocumentEnvironment {__sp_refrain} {}
+  % The environment opening may be followed by a [length], in fact part of its
+  % body, and will appear just after the opening of the verse environment and
+  % constitute its optional argument.
+  {
+    \begin{structureenv}
+    \begin{verse}
+  }
+  {
+    \end{verse}
+    \end{structureenv}
+  }
+%    \end{macrocode}
+% \end{environment}
+%
+% \begin{environment}{__sp_couplet}
+% This environment simply formats a song couplet. It is used in the user interface
+% \env{couplet} environment.
+%
+%    \begin{macrocode}
+\NewDocumentEnvironment {__sp_couplet} {}
+  % The environment opening may be followed by a [length], in fact part of its
+  % body, and will appear just after the opening of the verse environment and
+  % constitute its optional argument.
+  { \begin{verse} }
+  { \end{verse} }
+%    \end{macrocode}
+% \end{environment}
+%
+% \begin{environment}{__sp_special} {}
+%
+% \changes{v0.4.0}{2021/09/29}{Add an environment to format intro and final}
+%
+% This environments simply formats a song intro of final. It is used in the
+% user interface \env{intro} and \env{final} environments.
+%
+%    \begin{macrocode}
+\NewDocumentEnvironment {__sp_special} {}
+  % The environment opening may be followed by a [length], in fact part of its
+  % body, and will appear just after the opening of the verse environment and
+  % constitute its optional argument.
+  {
+    \begin{em}
+    \begin{verse}
+  }
+  {
+    \end{verse}
+    \end{em}
+  }
+%    \end{macrocode}
+% \end{environment}
+%
+% \subsubsection{Internal macros}
+% \changes{v0.3.0}{2021/09/26}{Define aux functions at top-level}
+%
+% These are macros that are used in the implementation of the \env{song}
+% environment.
+%
+% \begin{macro}{\__sp_song_refrain}
+%
+% This macro uses the \env{__sp_refrain} environment to format the current song
+% refrain.
+%
+%    \begin{macrocode}
+\tl_gset:Nn \__sp_song_refrain
+  {
+    % Do we know the width of the longest song line?
+    \dim_compare:nNnTF \g__sp_linewidth_dim {=} {0pt}
+      { \begin{__sp_refrain} }
+      { \begin{__sp_refrain} [\g__sp_linewidth_dim] }
+    \tl_use:N \g__sp_refrain_tl
+    \end{__sp_refrain}
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\__sp_song_couplet:n}
+%
+% This macro uses the \env{__sp_couplet} environment to a specified couplet of
+% the current song. It takes a single argument:
+%
+% \begin{arguments}
+%   \item index of the couplet to format.
+% \end{arguments}
+%
+%    \begin{macrocode}
+\cs_gset:Npn \__sp_song_couplet:n #1
+  {
+    % Do we know the width of the longest song line?
+    \dim_compare:nNnTF \g__sp_linewidth_dim {=} {0pt}
+      { \begin{__sp_couplet} }
+      { \begin{__sp_couplet} [\g__sp_linewidth_dim] }
+    \seq_item:Nn \g__sp_couplets_seq {#1}
+    \end{__sp_couplet}
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\__sp_song_couplets:n}
+%
+% This macro inserts an containing all couplets of the current song in an
+% \env{overprint} environment, in groups separated with \cs{onslide}
+% commands. It takes a single argument:
+%
+% \begin{arguments}
+%   \item number of couplets to show together on each slide.
+% \end{arguments}
+%
+%    \begin{macrocode}
+\cs_gset:Npn \__sp_song_couplets:n #1
+  {
+    \begin{overprint}
+    % Loop on all specified couplets
+    \int_step_inline:nn
+      { \seq_count:N \g__sp_couplet_indexes_seq }
+      {
+        % Before every #1 lines, i.e. when (##1 - 1) mod #1 == 0),
+        % insert an \onslide
+        \int_compare:nNnTF
+          { \int_mod:nn { \int_eval:n{##1 - 1} } {#1} } { = } { 0 }
+          { \onslide<+> }
+          { \vskip \stanzaskip }
+        \__sp_song_couplet:n { \seq_item:Nn \g__sp_couplet_indexes_seq {##1} }
+      }
+    \end{overprint}
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\__sp_song_intro}
+%
+% \changes{v0.4.0}{2021/09/29}{Add an environment to format current song intro}
+%
+% This macro uses the \env{__sp_special} environment to format the current song
+% intro.
+%
+%    \begin{macrocode}
+\tl_gset:Nn \__sp_song_intro
+  {
+    % Do we know the width of the longest song line?
+    \dim_compare:nNnTF \g__sp_linewidth_dim {=} {0pt}
+      { \begin{__sp_special} }
+      { \begin{__sp_special} [\g__sp_linewidth_dim] }
+    \tl_use:N \g__sp_intro_tl
+    \end{__sp_special}
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\__sp_song_final}
+%
+% \changes{v0.4.0}{2021/09/29}{Add an environment to format current song final}
+%
+% This macro uses the \env{__sp_refrain} environment to format the current song
+% final.
+%
+%    \begin{macrocode}
+\tl_gset:Nn \__sp_song_final
+  {
+    % Do we know the width of the longest song line?
+    \dim_compare:nNnTF \g__sp_linewidth_dim {=} {0pt}
+      { \begin{__sp_special} }
+      { \begin{__sp_special} [\g__sp_linewidth_dim] }
+    \tl_use:N \g__sp_final_tl
+    \end{__sp_special}
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\__sp_song}
+%
+% \changes{v0.4.0}{2021/09/29}{Include intro and final in complete song}
+%
+% This macro inserts the entire song, alternating refrain and couplets in an
+% \env{overprint} environment.
+%
+%    \begin{macrocode}
+\tl_gset:Nn \__sp_song
+  {
+    % Is there a song intro?
+    \tl_if_empty:NTF \g__sp_intro_tl
+    {}
+    {
+      \visible<1> {\__sp_song_intro}
+      % The combination of overprint with verse that comes next somehow adds
+      % extra vertical space that needs to be removed.
+      \vskip -\stanzaskip
+    }
+
+    \begin{overprint}
+
+    % Does the song begin with the refrain?
+    \bool_if:NTF \g__sp_refrain_first_bool
+      {
+        % If so, print an initial refrain
+        \onslide<+>
+        \__sp_song_refrain
+      }
+      {}
+
+    % Is there a refrain?
+    \tl_if_empty:NTF \g__sp_refrain_tl
+      {
+        % No refrain, loop on all specified couplets and insert them
+        \seq_map_inline:Nn
+          \g__sp_couplet_indexes_seq
+          {
+            \onslide<+>
+            \__sp_song_couplet:n {#1}
+          }
+      }
+      {
+        % There is a refrain, loop on all specified couplets and, each time,
+        % insert both a couplet and the refrain
+        \seq_map_inline:Nn
+          \g__sp_couplet_indexes_seq
+          {
+            \onslide<+>
+            \__sp_song_couplet:n {#1}
+            \onslide<+>
+            \__sp_song_refrain
+          }
+      }
+    \end{overprint}
+
+    % Is there a song final?
+    \tl_if_empty:NTF \g__sp_final_tl
+    {}
+    {
+      % Add extra spacing
+      \vskip \stanzaskip
+      \visible<.> {\__sp_song_final}
+    }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \subsection{User interface}
+%
+% These environments constitute our user interface. They allow the user to
+% define songs, refrains and couplets.
+%
+% \begin{environment}{refrain}
+% This environment handles a refrain :
+% \begin{itemize}
+%   \item outside of a song, it uses the \env{__sp_refrain} environment to
+%     directly format it ;
+%   \item inside a song, it stores it into \cs{g__sp_retrain_tl}, so it can
+%     be formatted by the end of the \env{song} environment.
+% \end{itemize}
+%
+%    \begin{macrocode}
+\NewDocumentEnvironment {refrain} { +b }
+  % The environment opening may be followed by a [length], in fact part of its
+  % body, and will appear just after the opening of the __sp_refrain
+  % environment and constitute its optional argument.
+  {
+    % Are we in a song?
+    \bool_if:NTF \g__sp_song_bool
+      {
+        % We are in a song, are we at its start?
+        \bool_if:NTF \g__sp_song_start_bool
+          {
+            % Indicate that we are no longer at the start of the song
+            \bool_gset_false:N\g__sp_song_start_bool
+            % and that the refrain comes first
+            \bool_gset_true:N\g__sp_refrain_first_bool
+          }
+          {}
+        % Anyway, store the refrain
+        \tl_gset:Nn \g__sp_refrain_tl {#1}
+      }
+      {
+        % We are not in a song, use __sp_refrain to format the refrain
+          \begin{__sp_refrain}
+            #1
+          \end{__sp_refrain}
+      }
+  }
+  {}
+%    \end{macrocode}
+% \end{environment}
+%
+% \begin{environment}{couplet}
+% This environment handles a couplet, in a similar way:
+% \begin{itemize}
+%   \item outside of a song, it uses the \env{__sp_couplet} environment to
+%     directly format it ;
+%   \item inside a song, it stores it by appending it to to
+%     \cs{g__sp_couplets_seq}, so it can be formatted by the end of the
+%     \env{song} environment.
+% \end{itemize}
+%
+%    \begin{macrocode}
+\NewDocumentEnvironment {couplet} { +b }
+  % The environment opening may be followed by a [length], in fact part of its
+  % body, and will appear just after the opening of the __sp_couplet
+  % environment and constitute its optional argument.
+  {
+    % Are we in a song?
+    \bool_if:NTF \g__sp_song_bool
+      {
+        % Are we at in a song, are we at its start?
+        \bool_if:NTF \g__sp_song_start_bool
+          {
+            % Indicate that we are no longer at the start of the song
+            \bool_gset_false:N \g__sp_song_start_bool
+            % and that the refrain does not come first
+            \bool_gset_false:N \g__sp_refrain_first_bool
+          }
+          {}
+        % Anyway, store this couplet
+        \seq_gput_right:Nn \g__sp_couplets_seq { {#1} }
+      }
+      {
+        % We are not in a song, use __sp_couplet to format this couplet
+          \begin{__sp_couplet}
+            #1
+          \end{__sp_couplet}
+      }
+  }
+  {}
+%    \end{macrocode}
+% \end{environment}
+%
+% \begin{environment}{intro}
+%
+% \changes{v0.4.0}{2021/09/29}{Add an \env{intro} environment}
+%
+% This environment handles a song intro, in a similar way:
+% \begin{itemize}
+%   \item outside of a song, it uses the \env{__sp_special} environment to
+%     directly format it;
+%   \item inside a song, it stores it  into \cs{g__sp_intro_tl} so it can be
+%     formatted by the end of the \env{song} environment.
+% \end{itemize}
+%
+%    \begin{macrocode}
+\NewDocumentEnvironment {intro} { +b }
+  % The environment opening may be followed by a [length], in fact part of its
+  % body, and will appear just after the opening of the __sp_special
+  % environment and constitute its optional argument.
+  {
+    % Are we in a song?
+    \bool_if:NTF \g__sp_song_bool
+      {
+        % We are in a song, store its intro
+        \tl_gset:Nn \g__sp_intro_tl {#1}
+      }
+      {
+        % We are not in a song, use __sp_special to format the intro
+        \begin{__sp_special}
+          #1
+        \end{__sp_special}
+      }
+  }
+  {}
+%    \end{macrocode}
+% \end{environment}
+%
+% \begin{environment}{final}
+%
+% \changes{v0.4.0}{2021/09/29}{Add a \env{final} environment}
+%
+% This environment handles a song final, in a similar way:
+% \begin{itemize}
+%   \item outside of a song, it uses the \env{__sp_special} environment to
+%     directly format it;
+%   \item inside a song, it stores it  into \cs{g__sp_final_tl} so it can be
+%     formatted by the end of the \env{song} environment.
+% \end{itemize}
+%
+%    \begin{macrocode}
+\NewDocumentEnvironment {final} { +b }
+  % The environment opening may be followed by a [length], in fact part of its
+  % body, and will appear just after the opening of the __sp_special
+  % environment and constitute its optional argument.
+  {
+    % Are we in a song?
+    \bool_if:NTF \g__sp_song_bool
+      {
+        % We are in a song, store its intro
+        \tl_gset:Nn \g__sp_final_tl {#1}
+      }
+      {
+        % We are not in a song, use __sp_special to format the intro
+        \begin{__sp_special}
+          #1
+        \end{__sp_special}
+      }
+  }
+  {}
+%    \end{macrocode}
+% \end{environment}
+%
+% \begin{macro}{\longest}
+% This macro measures the length of a song line and stores it, so it can be
+% used by the \env{song} environment to properly center refrain and couplets.
+% It takes a single argument:
+%
+% \begin{arguments}
+%   \item a song line to measure.
+% \end{arguments}
+%
+%    \begin{macrocode}
+\NewDocumentCommand {\longest} { m } { \settowidth {\g__sp_linewidth_dim} {#1} }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{environment}{song}
+%
+% This environment is used as a container for entire songs. On opening, it does several things:
+%   \begin{enumerate}
+%     \item its stores its arguments into variables with a descriptive name;
+%     \item it clears out any previously stored refrain, couplet, intro, final
+%       and longest song line;
+%     \item it sets the \cs{g__sp_song_bool} variable to indicate that we
+%       are inside a song, which will alter the behaviour of the
+%       \env{refrain} and \env{couplet} environments so they record their
+%       content rather than directly formatting it into the document;
+%     \item it sets the \cs{g__sp_song_start_bool} variable to indicate that
+%       we are at the start of the song, which will allow the next
+%       \env{refrain} or \env{couplet} to tell if the song starts with the
+%       refrain or with a couplet;
+%   \end{enumerate}
+%
+% This environment takes two arguments:
+%
+% \begin{arguments}
+%   \item number of stanzas (counting couplets and refrain, when there is one) per slide;
+%   \item list of couplets to include (defaults to all), for instance |1,3,4|.
+% \end{arguments}
+%
+%    \begin{macrocode}
+\NewDocumentEnvironment {song} { m o }
+  % {number of stanzas per slide (1 or 2)}
+  % [list of couplets to include (defaults to all)]
+  {
+    % Put arguments into variables with understandable names
+    \int_gset_eq:NN {\g__sp_stanzas_per_slide_int} {#1}
+    \IfNoValueTF {#2}
+      { \seq_gclear:N \g__sp_couplet_indexes_seq }
+      { \seq_gset_from_clist:Nn \g__sp_couplet_indexes_seq {#2} }
+  
+    % Clear out intro, refrain, couplet, final and longest song line
+    \tl_gclear:N \g__sp_intro_tl
+    \tl_gclear:N \g__sp_refrain_tl
+    \seq_gclear:N \g__sp_couplets_seq
+    \tl_gclear:N \g__sp_final_tl
+    \dim_zero:N {\g__sp_linewidth_dim}
+  
+    % Indicate that we are in a song, and at its start
+    \bool_gset_true:N \g__sp_song_bool
+    \bool_gset_true:N \g__sp_song_start_bool
+  }
+%    \end{macrocode}
+%
+% And on closing:
+% \begin{itemize}
+%   \item if no list of couplet indexes to use have been given, it generates one
+%     covering all couplets in order;
+%   \item it uses internal functions to insert the intro, refrain, couplets and
+%     final into the document, in the right order according to the song
+%     structure (refrain or couplet first) and to the formatting instructions
+%     (one or two stanzas per slide).
+% \end{itemize}
+%
+% \changes{v0.4.0}{2021/09/29}{Include intro and final in formatted song}
+%
+%    \begin{macrocode}
+  {
+    % Have we been given indexes of specific couplets to use?
+    \seq_if_empty:NTF \g__sp_couplet_indexes_seq
+      {
+        % If not, generate it from the list of couplets
+        \int_step_inline:nn
+          { \seq_count:N \g__sp_couplets_seq }
+          { \seq_gput_right:Nn \g__sp_couplet_indexes_seq {##1} }
+      }
+      {}
+
+    % Now we actually start inserting things into the document.
+    % How many stanzas per side did the user request?
+    \int_compare:nNnTF \g__sp_stanzas_per_slide_int {>} {1}
+      {
+        % More than one stanza per slide
+        %
+        % Is there an intro?
+        \tl_if_empty:NTF \g__sp_intro_tl
+          {}
+          {
+            \visible<1> {\__sp_song_intro}
+            % Adjust vertical spacing depending on whether the refrain or the
+            % couplets follow.
+            \bool_if:NTF\g__sp_refrain_first_bool
+              {
+                % Refrain comes next, add extra space
+                \vskip \parsep
+              }
+              {
+                % Couplets come next, the combination of their overprint and
+                % verse environment somehow adds extra vertical space that
+                % needs to be removed.
+                \vskip -\stanzaskip
+              }
+          }
+
+        % Is there a refrain?
+        \tl_if_empty:NTF \g__sp_refrain_tl
+          {
+            % If there is no refrain, we use \__sp_song_couplets:n to write the
+            % couplets, \g__sp_stanzas_per_slide_int at a time.
+            \__sp_song_couplets:n { \int_use:N \g__sp_stanzas_per_slide_int }
+          }
+          {
+            % If there is a refrain, we use \__sp_song_refrain to write the
+            % refrain and \__sp_song_couplets:n to write overprint with all
+            % couplets.
+
+            % Does the song begin with the refrain?
+            \bool_if:NTF\g__sp_refrain_first_bool
+              {
+                \__sp_song_refrain
+                \vskip -\stanzaskip
+                \__sp_song_couplets:n 1
+              }
+              {
+                \__sp_song_couplets:n 1
+                \vskip \stanzaskip
+                \__sp_song_refrain
+              }
+          }
+
+        % Is there a final?
+        \tl_if_empty:NTF \g__sp_final_tl
+          {}
+          {
+            % Adjust vertical spacing depending on whether we follow the
+            % refrain or the couplets.
+            \tl_if_empty:NTF \g__sp_refrain_tl
+              {
+                % No refrain, we follow the couplets, add extra space
+                \vskip \stanzaskip
+              }
+              {
+                % There was a refrain, did it come first?
+                \bool_if:NTF \g__sp_refrain_first_bool
+                {
+                  % Refrain came first, we follow the couplets, add extra space
+                  \vskip \stanzaskip
+                }
+                {
+                  % Refrain came last, we follow it, add extra space
+                  \vskip \parsep
+                }
+              }
+            \visible<.> {\__sp_song_final}
+          }
+      }
+      {
+        % If the user requested one stanza per slide, we use \__sp_song to
+        % write the entire song in a single overprint environment.
+        \__sp_song
+      }
+    % Indicate that we are no longer in a song
+    \bool_gset_false:N\g__sp_song_bool
+  }
+%    \end{macrocode}
+% \end{environment}
+%
+% \begin{macro}{\inputsong}
+% \changes{v1.0.0}{2021/11/07}{Add a \cs{inputsong} command}
+%
+% This macro starts a \env{song} environment and \cs{input}s the song content
+% from an external file.
+%
+%    \begin{macrocode}
+\NewDocumentCommand {\inputsong} { m m o }
+  {
+    \IfNoValueTF {#3}
+      { \begin{song} {#2} }
+      { \begin{song} {#2} [#3] }
+    \input{#1}
+    \end{song}
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \subsection{Wrapping up}
+%
+% Now that we have defined everything we need, we can leave the \textsf{expl3}
+% syntax and return to normal \TeX{} syntax:
+%
+%    \begin{macrocode}
+\ExplSyntaxOff
+%    \end{macrocode}
+%
+% \Finale
+\endinput


Property changes on: trunk/Master/texmf-dist/source/latex/songproj/songproj.dtx
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/source/latex/songproj/songproj.ins
===================================================================
--- trunk/Master/texmf-dist/source/latex/songproj/songproj.ins	                        (rev 0)
+++ trunk/Master/texmf-dist/source/latex/songproj/songproj.ins	2022-11-07 20:29:26 UTC (rev 64966)
@@ -0,0 +1,83 @@
+%%
+%% Copyright (C) 2022 by Tanguy Ortolo <tanguy+latex at ortolo.eu>
+%%
+%% Redistribution and use in source and binary forms, with or without
+%% modification, are permitted provided that the following conditions are met:
+%% 1. Redistributions of source code must retain the above copyright notice,
+%%    this list of conditions and the following disclaimer.
+%% 2. Redistributions in binary form must reproduce the above copyright notice,
+%%    this list of conditions and the following disclaimer in the documentation
+%%    and/or other materials provided with the distribution.
+%% 
+%% 3. Neither the name of the copyright holder nor the names of its
+%%    contributors may be used to endorse or promote products derived from this
+%%    software without specific prior written permission.
+%% 
+%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+%% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+%% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+%% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+%% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+%% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+%% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+%% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+%% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+%% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+%% POSSIBILITY OF SUCH DAMAGE.
+%%
+
+\input docstrip.tex
+\keepsilent
+
+\usedir{tex/latex/songproj}
+
+\preamble
+
+This is a generated file.
+
+Copyright (C) 2022 by Tanguy Ortolo <tanguy+latex at ortolo.eu>
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+   contributors may be used to endorse or promote products derived from this
+   software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+\endpreamble
+
+\generate{\file{songproj.sty}{\from{songproj.dtx}{package}}}
+
+\obeyspaces
+\Msg{*************************************************************}
+\Msg{*                                                           *}
+\Msg{* To finish the installation you have to move the following *}
+\Msg{* file into a directory searched by TeX:                    *}
+\Msg{*                                                           *}
+\Msg{*     songproj.sty                                          *}
+\Msg{*                                                           *}
+\Msg{* To produce the documentation run the file songproj.dtx    *}
+\Msg{* through LaTeX.                                            *}
+\Msg{*                                                           *}
+\Msg{* Happy TeXing!                                             *}
+\Msg{*                                                           *}
+\Msg{*************************************************************}
+
+\endbatchfile

Added: trunk/Master/texmf-dist/tex/latex/songproj/songproj.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/songproj/songproj.sty	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/latex/songproj/songproj.sty	2022-11-07 20:29:26 UTC (rev 64966)
@@ -0,0 +1,421 @@
+%%
+%% This is file `songproj.sty',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% songproj.dtx  (with options: `package')
+%% 
+%% This is a generated file.
+%% 
+%% Copyright (C) 2022 by Tanguy Ortolo <tanguy+latex at ortolo.eu>
+%% 
+%% Redistribution and use in source and binary forms, with or without
+%% modification, are permitted provided that the following conditions are met:
+%% 1. Redistributions of source code must retain the above copyright notice,
+%%    this list of conditions and the following disclaimer.
+%% 2. Redistributions in binary form must reproduce the above copyright notice,
+%%    this list of conditions and the following disclaimer in the documentation
+%%    and/or other materials provided with the distribution.
+%% 
+%% 3. Neither the name of the copyright holder nor the names of its
+%%    contributors may be used to endorse or promote products derived from this
+%%    software without specific prior written permission.
+%% 
+%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+%% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+%% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+%% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+%% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+%% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+%% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+%% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+%% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+%% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+%% POSSIBILITY OF SUCH DAMAGE.
+%% 
+\NeedsTeXFormat{LaTeX2e}[2020/10/01]
+\ProvidesPackage{songproj}
+    [2022/11/07 v1.0.1 Song projection]
+\RequirePackage{expl3}
+\RequirePackage{xparse}
+\RequirePackage{verse}
+\ExplSyntaxOn
+\bool_new:N \g__sp_song_bool             % are we in a song?
+\bool_new:N \g__sp_song_start_bool       % are we at the start of a song?
+\bool_new:N \g__sp_refrain_first_bool    % does current song start with the
+                                         % refrain?
+\int_new:N  \g__sp_stanzas_per_slide_int % number of stanzas to show on each
+                                         % slide (1 or 2)
+\dim_new:N  \g__sp_linewidth_dim         % length of the longest line in current
+                                         % song
+\tl_new:N   \g__sp_intro_tl              % current song intro
+\tl_new:N   \g__sp_refrain_tl            % current song refrain
+\seq_new:N  \g__sp_couplets_seq          % current song couplets
+\tl_new:N   \g__sp_final_tl              % current song final
+\seq_new:N  \g__sp_couplet_indexes_seq   % indexes of couplets to include
+\NewDocumentEnvironment {__sp_refrain} {}
+  % The environment opening may be followed by a [length], in fact part of its
+  % body, and will appear just after the opening of the verse environment and
+  % constitute its optional argument.
+  {
+    \begin{structureenv}
+    \begin{verse}
+  }
+  {
+    \end{verse}
+    \end{structureenv}
+  }
+\NewDocumentEnvironment {__sp_couplet} {}
+  % The environment opening may be followed by a [length], in fact part of its
+  % body, and will appear just after the opening of the verse environment and
+  % constitute its optional argument.
+  { \begin{verse} }
+  { \end{verse} }
+\NewDocumentEnvironment {__sp_special} {}
+  % The environment opening may be followed by a [length], in fact part of its
+  % body, and will appear just after the opening of the verse environment and
+  % constitute its optional argument.
+  {
+    \begin{em}
+    \begin{verse}
+  }
+  {
+    \end{verse}
+    \end{em}
+  }
+\tl_gset:Nn \__sp_song_refrain
+  {
+    % Do we know the width of the longest song line?
+    \dim_compare:nNnTF \g__sp_linewidth_dim {=} {0pt}
+      { \begin{__sp_refrain} }
+      { \begin{__sp_refrain} [\g__sp_linewidth_dim] }
+    \tl_use:N \g__sp_refrain_tl
+    \end{__sp_refrain}
+  }
+\cs_gset:Npn \__sp_song_couplet:n #1
+  {
+    % Do we know the width of the longest song line?
+    \dim_compare:nNnTF \g__sp_linewidth_dim {=} {0pt}
+      { \begin{__sp_couplet} }
+      { \begin{__sp_couplet} [\g__sp_linewidth_dim] }
+    \seq_item:Nn \g__sp_couplets_seq {#1}
+    \end{__sp_couplet}
+  }
+\cs_gset:Npn \__sp_song_couplets:n #1
+  {
+    \begin{overprint}
+    % Loop on all specified couplets
+    \int_step_inline:nn
+      { \seq_count:N \g__sp_couplet_indexes_seq }
+      {
+        % Before every #1 lines, i.e. when (##1 - 1) mod #1 == 0),
+        % insert an \onslide
+        \int_compare:nNnTF
+          { \int_mod:nn { \int_eval:n{##1 - 1} } {#1} } { = } { 0 }
+          { \onslide<+> }
+          { \vskip \stanzaskip }
+        \__sp_song_couplet:n { \seq_item:Nn \g__sp_couplet_indexes_seq {##1} }
+      }
+    \end{overprint}
+  }
+\tl_gset:Nn \__sp_song_intro
+  {
+    % Do we know the width of the longest song line?
+    \dim_compare:nNnTF \g__sp_linewidth_dim {=} {0pt}
+      { \begin{__sp_special} }
+      { \begin{__sp_special} [\g__sp_linewidth_dim] }
+    \tl_use:N \g__sp_intro_tl
+    \end{__sp_special}
+  }
+\tl_gset:Nn \__sp_song_final
+  {
+    % Do we know the width of the longest song line?
+    \dim_compare:nNnTF \g__sp_linewidth_dim {=} {0pt}
+      { \begin{__sp_special} }
+      { \begin{__sp_special} [\g__sp_linewidth_dim] }
+    \tl_use:N \g__sp_final_tl
+    \end{__sp_special}
+  }
+\tl_gset:Nn \__sp_song
+  {
+    % Is there a song intro?
+    \tl_if_empty:NTF \g__sp_intro_tl
+    {}
+    {
+      \visible<1> {\__sp_song_intro}
+      % The combination of overprint with verse that comes next somehow adds
+      % extra vertical space that needs to be removed.
+      \vskip -\stanzaskip
+    }
+
+    \begin{overprint}
+
+    % Does the song begin with the refrain?
+    \bool_if:NTF \g__sp_refrain_first_bool
+      {
+        % If so, print an initial refrain
+        \onslide<+>
+        \__sp_song_refrain
+      }
+      {}
+
+    % Is there a refrain?
+    \tl_if_empty:NTF \g__sp_refrain_tl
+      {
+        % No refrain, loop on all specified couplets and insert them
+        \seq_map_inline:Nn
+          \g__sp_couplet_indexes_seq
+          {
+            \onslide<+>
+            \__sp_song_couplet:n {#1}
+          }
+      }
+      {
+        % There is a refrain, loop on all specified couplets and, each time,
+        % insert both a couplet and the refrain
+        \seq_map_inline:Nn
+          \g__sp_couplet_indexes_seq
+          {
+            \onslide<+>
+            \__sp_song_couplet:n {#1}
+            \onslide<+>
+            \__sp_song_refrain
+          }
+      }
+    \end{overprint}
+
+    % Is there a song final?
+    \tl_if_empty:NTF \g__sp_final_tl
+    {}
+    {
+      % Add extra spacing
+      \vskip \stanzaskip
+      \visible<.> {\__sp_song_final}
+    }
+  }
+\NewDocumentEnvironment {refrain} { +b }
+  % The environment opening may be followed by a [length], in fact part of its
+  % body, and will appear just after the opening of the __sp_refrain
+  % environment and constitute its optional argument.
+  {
+    % Are we in a song?
+    \bool_if:NTF \g__sp_song_bool
+      {
+        % We are in a song, are we at its start?
+        \bool_if:NTF \g__sp_song_start_bool
+          {
+            % Indicate that we are no longer at the start of the song
+            \bool_gset_false:N\g__sp_song_start_bool
+            % and that the refrain comes first
+            \bool_gset_true:N\g__sp_refrain_first_bool
+          }
+          {}
+        % Anyway, store the refrain
+        \tl_gset:Nn \g__sp_refrain_tl {#1}
+      }
+      {
+        % We are not in a song, use __sp_refrain to format the refrain
+          \begin{__sp_refrain}
+            #1
+          \end{__sp_refrain}
+      }
+  }
+  {}
+\NewDocumentEnvironment {couplet} { +b }
+  % The environment opening may be followed by a [length], in fact part of its
+  % body, and will appear just after the opening of the __sp_couplet
+  % environment and constitute its optional argument.
+  {
+    % Are we in a song?
+    \bool_if:NTF \g__sp_song_bool
+      {
+        % Are we at in a song, are we at its start?
+        \bool_if:NTF \g__sp_song_start_bool
+          {
+            % Indicate that we are no longer at the start of the song
+            \bool_gset_false:N \g__sp_song_start_bool
+            % and that the refrain does not come first
+            \bool_gset_false:N \g__sp_refrain_first_bool
+          }
+          {}
+        % Anyway, store this couplet
+        \seq_gput_right:Nn \g__sp_couplets_seq { {#1} }
+      }
+      {
+        % We are not in a song, use __sp_couplet to format this couplet
+          \begin{__sp_couplet}
+            #1
+          \end{__sp_couplet}
+      }
+  }
+  {}
+\NewDocumentEnvironment {intro} { +b }
+  % The environment opening may be followed by a [length], in fact part of its
+  % body, and will appear just after the opening of the __sp_special
+  % environment and constitute its optional argument.
+  {
+    % Are we in a song?
+    \bool_if:NTF \g__sp_song_bool
+      {
+        % We are in a song, store its intro
+        \tl_gset:Nn \g__sp_intro_tl {#1}
+      }
+      {
+        % We are not in a song, use __sp_special to format the intro
+        \begin{__sp_special}
+          #1
+        \end{__sp_special}
+      }
+  }
+  {}
+\NewDocumentEnvironment {final} { +b }
+  % The environment opening may be followed by a [length], in fact part of its
+  % body, and will appear just after the opening of the __sp_special
+  % environment and constitute its optional argument.
+  {
+    % Are we in a song?
+    \bool_if:NTF \g__sp_song_bool
+      {
+        % We are in a song, store its intro
+        \tl_gset:Nn \g__sp_final_tl {#1}
+      }
+      {
+        % We are not in a song, use __sp_special to format the intro
+        \begin{__sp_special}
+          #1
+        \end{__sp_special}
+      }
+  }
+  {}
+\NewDocumentCommand {\longest} { m } { \settowidth {\g__sp_linewidth_dim} {#1} }
+\NewDocumentEnvironment {song} { m o }
+  % {number of stanzas per slide (1 or 2)}
+  % [list of couplets to include (defaults to all)]
+  {
+    % Put arguments into variables with understandable names
+    \int_gset_eq:NN {\g__sp_stanzas_per_slide_int} {#1}
+    \IfNoValueTF {#2}
+      { \seq_gclear:N \g__sp_couplet_indexes_seq }
+      { \seq_gset_from_clist:Nn \g__sp_couplet_indexes_seq {#2} }
+
+    % Clear out intro, refrain, couplet, final and longest song line
+    \tl_gclear:N \g__sp_intro_tl
+    \tl_gclear:N \g__sp_refrain_tl
+    \seq_gclear:N \g__sp_couplets_seq
+    \tl_gclear:N \g__sp_final_tl
+    \dim_zero:N {\g__sp_linewidth_dim}
+
+    % Indicate that we are in a song, and at its start
+    \bool_gset_true:N \g__sp_song_bool
+    \bool_gset_true:N \g__sp_song_start_bool
+  }
+  {
+    % Have we been given indexes of specific couplets to use?
+    \seq_if_empty:NTF \g__sp_couplet_indexes_seq
+      {
+        % If not, generate it from the list of couplets
+        \int_step_inline:nn
+          { \seq_count:N \g__sp_couplets_seq }
+          { \seq_gput_right:Nn \g__sp_couplet_indexes_seq {##1} }
+      }
+      {}
+
+    % Now we actually start inserting things into the document.
+    % How many stanzas per side did the user request?
+    \int_compare:nNnTF \g__sp_stanzas_per_slide_int {>} {1}
+      {
+        % More than one stanza per slide
+        %
+        % Is there an intro?
+        \tl_if_empty:NTF \g__sp_intro_tl
+          {}
+          {
+            \visible<1> {\__sp_song_intro}
+            % Adjust vertical spacing depending on whether the refrain or the
+            % couplets follow.
+            \bool_if:NTF\g__sp_refrain_first_bool
+              {
+                % Refrain comes next, add extra space
+                \vskip \parsep
+              }
+              {
+                % Couplets come next, the combination of their overprint and
+                % verse environment somehow adds extra vertical space that
+                % needs to be removed.
+                \vskip -\stanzaskip
+              }
+          }
+
+        % Is there a refrain?
+        \tl_if_empty:NTF \g__sp_refrain_tl
+          {
+            % If there is no refrain, we use \__sp_song_couplets:n to write the
+            % couplets, \g__sp_stanzas_per_slide_int at a time.
+            \__sp_song_couplets:n { \int_use:N \g__sp_stanzas_per_slide_int }
+          }
+          {
+            % If there is a refrain, we use \__sp_song_refrain to write the
+            % refrain and \__sp_song_couplets:n to write overprint with all
+            % couplets.
+
+            % Does the song begin with the refrain?
+            \bool_if:NTF\g__sp_refrain_first_bool
+              {
+                \__sp_song_refrain
+                \vskip -\stanzaskip
+                \__sp_song_couplets:n 1
+              }
+              {
+                \__sp_song_couplets:n 1
+                \vskip \stanzaskip
+                \__sp_song_refrain
+              }
+          }
+
+        % Is there a final?
+        \tl_if_empty:NTF \g__sp_final_tl
+          {}
+          {
+            % Adjust vertical spacing depending on whether we follow the
+            % refrain or the couplets.
+            \tl_if_empty:NTF \g__sp_refrain_tl
+              {
+                % No refrain, we follow the couplets, add extra space
+                \vskip \stanzaskip
+              }
+              {
+                % There was a refrain, did it come first?
+                \bool_if:NTF \g__sp_refrain_first_bool
+                {
+                  % Refrain came first, we follow the couplets, add extra space
+                  \vskip \stanzaskip
+                }
+                {
+                  % Refrain came last, we follow it, add extra space
+                  \vskip \parsep
+                }
+              }
+            \visible<.> {\__sp_song_final}
+          }
+      }
+      {
+        % If the user requested one stanza per slide, we use \__sp_song to
+        % write the entire song in a single overprint environment.
+        \__sp_song
+      }
+    % Indicate that we are no longer in a song
+    \bool_gset_false:N\g__sp_song_bool
+  }
+\NewDocumentCommand {\inputsong} { m m o }
+  {
+    \IfNoValueTF {#3}
+      { \begin{song} {#2} }
+      { \begin{song} {#2} [#3] }
+    \input{#1}
+    \end{song}
+  }
+\ExplSyntaxOff
+\endinput
+%%
+%% End of file `songproj.sty'.


Property changes on: trunk/Master/texmf-dist/tex/latex/songproj/songproj.sty
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Modified: trunk/Master/tlpkg/bin/tlpkg-ctan-check
===================================================================
--- trunk/Master/tlpkg/bin/tlpkg-ctan-check	2022-11-07 20:28:16 UTC (rev 64965)
+++ trunk/Master/tlpkg/bin/tlpkg-ctan-check	2022-11-07 20:29:26 UTC (rev 64966)
@@ -742,7 +742,7 @@
     slantsc slideshow
     smalltableof smart-eqn smartdiagram smartref smartunits smflatex
     snapshot snaptodo snotez
-    songbook songs sort-by-letters soton soul soulutf8 soulpos
+    songbook songproj songs sort-by-letters soton soul soulutf8 soulpos
     soup sourcecodepro sourcesanspro
     sourceserifpro
     spacekern spacingtricks spalign spark-otf sparklines spath3 spbmark

Modified: trunk/Master/tlpkg/tlpsrc/collection-music.tlpsrc
===================================================================
--- trunk/Master/tlpkg/tlpsrc/collection-music.tlpsrc	2022-11-07 20:28:16 UTC (rev 64965)
+++ trunk/Master/tlpkg/tlpsrc/collection-music.tlpsrc	2022-11-07 20:29:26 UTC (rev 64966)
@@ -34,6 +34,7 @@
 depend pmx
 depend pmxchords
 depend songbook
+depend songproj
 depend songs
 depend xml2pmx
 depend xpiano

Added: trunk/Master/tlpkg/tlpsrc/songproj.tlpsrc
===================================================================


More information about the tex-live-commits mailing list.