Préparation d'un module pour la projection de textes de chants

6 views
Skip to first unread message

Tanguy Ortolo

unread,
Sep 19, 2022, 9:36:59 AMSep 19
to
Bonjour à tous,

Dans mon église locale, nous avons un vidéoprojecteur qui sert à
afficher les textes des chants utilisés. Ça permet de consommer de
l'électricité plutôt que des tonnes de papier (je n'ai pas vérifié si
les bilans financier, énergétique et carbone étaient favorables, mais ce
n'est pas le sujet). Et ça nécessite surtout de préparer, pour chaque
office, un diaporama avec les textes de chants.

Vous me voyez venir, j'imagine : PowerPoint et LibreOffice Impress me
conviennent fort peu, en particulier avec l'absence de notion de
surcouche, qui implique beaucoup de manipulations manuelles pour aligner
les couplets d'une diapo sur la suivante. Après en avoir soupé avec ces
WYSIWYG, je viens donc enfin de réaliser de quoi faire ça avec Beamer.

Pour vous donner un peu de contexte, il s'agit d'afficher le texte de
chants constitués d'un refrain et de couplets en alternance, en
commençant soit par un couplet (C R C R C R), soit directement par le
refrain (R C R C R C R). Certains chants n'ont d'ailleurs que des
couplets. Si les couplets et le refrain ne sont pas trop longs, on peut
afficher sur une même diapo un couplet et le refrain. Sinon, on
n'affiche qu'un couplet, puis, sur la diapo suivante, le refrain, et
ainsi de suite.

L'utilisation visée consiste à écrire le texte des chants dans des
fichiers séparés, avec un balisage de ce genre :

``` latex ave_maria_lourdes.tex
% Vers le plus long, utilisé pour le centrage
\longest{Dans l'eau pure et sainte}

\begin{couplet}
Ô Vierge Marie \\
Le peuple chrétien \\
À Lourdes vous prie \\
Chez vous il revient. \\
\end{couplet}

\begin{refrain}
Ave, ave, ave Maria. (bis) \\
\end{refrain}

\begin{couplet}
Le fond de la roche \\
S'éclaire à l'instant \\
La Dame s'approche \\
Fait signe à l'enfant. \\
\end{couplet}

\begin{couplet}
« Venez, je vous prie \\
Ici, quinze fois \\
Avec vos amies \\
Entendre ma voix ». \\
\end{couplet}

\begin{couplet}
Avec insistance \\
La Dame, trois fois \\
A dit : « Pénitence ». \\
Chrétien, c'est pour toi ! \\
\end{couplet}

% Ce chant est plus long, mais c'est sans intérêt pour cet exemple
\end{chant}
```

Ce genre de fichier sera ensuite à importer dans un diaporama, comme
ceci :

``` latex ecm.tex
\documentclass[12pt]{beamer}
\usepackage[french]{babel}
\usepackage{songproj}

\begin{document}
\begin{frame}
\begin{song}{2}[1,2,3]
% Deux strophes sur l'écran (couplet et refrain)
% Utiliser les couplets 1, 2 et 3
\input{ave_maria_lourdes}
\end{song}
\end{frame}
\end{document}
```

Le code de mon extension détermine s'il y a un refrain au début ou après
un premier couplet, pour construire un transparent de ce genre :

```latex
\begin{frame}
\settowidth{\versewidth}{Dans l'eau pure et sainte}
\vfill \vfill
\begin{overprint}
\onslide<+>
\begin{verse}[\versewidth]
Ô Vierge Marie \\
Le peuple chrétien \\
À Lourdes vous prie \\
Chez vous il revient. \\
\end{verse}
\onslide<+>
\begin{verse}[\versewidth]
Le fond de la roche \\
S'éclaire à l'instant \\
La Dame s'approche \\
Fait signe à l'enfant. \\
\end{verse}
% Et ainsi de suite pour les autres couplets
\end{overprint}
\vfill
\begin{verse}[\versewidth]
Ave, ave, ave Maria. (bis) \\
\end{verse}
\vfill \vfill
\end{frame}
```

J'en viens au code de mon extension (c'est un peu long, vous vous en
doutez). Comme vous pourrez le voir, j'utilise largement des concepts de
LaTeX3, que j'ai trouvés très utiles. Pour info, j'ai conservé les
termes de « refrain » et « couplet » en français, parce qu'ils sont déjà
utilisés en anglais, et que les termes anglo-anglais correspondants,
soit “chorus” et “verse”, sont extrêmement ambigus (en particulier
“verse”, qui peut également signifier « vers » ou « strophe »).

Que pensez-vous de mon code ci-dessous ? Je m'interroge en particulier
sur les points suivants :
- Le nom : j'ai appelé ça “songproj” mais on devrait sans doute pouvoir
trouver mieux.
- L'utilisation de variables globales : c'est simple et acceptable parce
qu'il serait à mon avis tout à fait absurde de vouloir définir un
chant dans un chant. Mais s'il s'avère plus propre ou plus pertinent
d'utiliser des affectations locales, je suis preneur de suggestions
sur la façon de faire cela.
- J'aimerais bien prendre en charge des listes de numéros de couplets
comme « 1-2, 6-7 », voire « -2, 6- » mais j'ai peur que ce soit un peu
trop compliqué à faire pour une utilité assez faible (on chante
généralement entre deux et cinq couplets, donc rien d'horrible à
énumérer.
- À plusieurs endroits, je cherche à propager un argument optionnel d'une
fonction à une autre. Dans l'environnement `refrain` par exemple, je
passe l'argument optionnel `#1` à l'environnement `simple_refrain`.
J'utilise pour cela `\IfNoValueTF{#1}` pour rappeler, selon le cas,
`\begin{simple_refrain}` ou `\begin{simple_refrain}[#1]`. J'aimerais
bien simplifier cela.
- Ça ne permet pas d'afficher un chant sans refrain avec deux couplets
par écran, ce qui devra faire l'objet d'une amélioration future.
- Il me reste à déterminer comment documenter et de publier cela, parce
que c'est la première fois que j'écris une extension qui peut
intéresser plusieurs personnes sur la planète. :-)

Ah, et évidemment, vous pouvez l'essayer avec `ecm.tex` et
`ave_maria_lourdes.tex` précités.

```latex songproj.sty
\NeedsTeXFormat{LaTeX2e}[2021/02/02]
\ProvidesPackage{songproj}[2021/09/19 Song projection]
\RequirePackage{expl3}
\RequirePackage{xparse}
\RequirePackage{verse}

\ExplSyntaxOn

\bool_new:N\g_my_song_bool % are we in a song?
\bool_new:N\g_my_song_start_bool % are we at the start of a song?
\bool_new:N\g_my_refrain_first_bool % does the song start with the refrain?
\int_new:N\g_my_stanzas_per_slide_int % number of stanzas to show on each slide (1 or 2)
\dim_new:N\g_my_linewidth_dim % length of the longest song line
\seq_new:N\g_my_couplet_indexes_seq % indexes of song couplets to include
\seq_new:N\g_my_couplets_seq % list of the song's couplets
\tl_new:N\g_my_refrain_tl % song refrain

% Following variables are used as functions in the implementation of the song
% environment
\tl_new:N\g_my_song_refrain % type the refrain
\cs_new:Npn\g_my_song_couplet:n#1{} % type the n-th verse
\cs_new:Npn\g_my_song_couplet_refrain:n#1{} % type the n-th verse and refrain
\tl_new:N\g_my_song_couplets % type all verses
\tl_new:N\g_my_song % type refrain and verses

% Type the refrain
\NewDocumentEnvironment{simple_refrain}{o}
{
\begin{structureenv}
\IfNoValueTF{#1}
{\begin{verse}}
{\begin{verse}[#1]}
}
{
\end{verse}
\end{structureenv}
}

% Type a couplet
\NewDocumentEnvironment{simple_couplet}{o}
{
\IfNoValueTF{#1}
{\begin{verse}}
{\begin{verse}[#1]}
}
{\end{verse}}

% Define current song's refrain
\NewDocumentEnvironment{refrain_chant}{+b}
{\tl_gset:Nn\g_my_refrain_tl{#1}} {}

% Add a couplet to the current song
\NewDocumentEnvironment{couplet_chant}{+b}
{\seq_gput_right:Nn\g_my_couplets_seq{{#1}}} {}

% Handle a refrain :
% - outside of a song, simply type it ;
% - inside a song, define its refrain to type it later.
\NewDocumentEnvironment{refrain}{o}
{
\bool_if:NTF\g_my_song_bool
{
\bool_if:NTF\g_my_song_start_bool
{
\bool_gset_false:N\g_my_song_start_bool
\bool_gset_true:N\g_my_refrain_first_bool
}
{}
\begin{refrain_chant}
}
{
\IfNoValueTF{#1}
{\begin{simple_refrain}}
{\begin{simple_refrain}[#1]}
}
}
{
\bool_if:NTF\g_my_song_bool
{\end{refrain_chant}}
{\end{simple_refrain}}
}

% Handle a couplet :
% - outside of a song, simply type it ;
% - inside a song, add it to its couplets to type it later.
\NewDocumentEnvironment{couplet}{o}
{
\bool_if:NTF\g_my_song_bool
{
\bool_if:NTF\g_my_song_start_bool
{
\bool_gset_false:N\g_my_song_start_bool
\bool_gset_false:N\g_my_refrain_first_bool
} {}
\begin{couplet_chant}
}
{
\IfNoValueTF{#1}
{\begin{simple_couplet}}
{\begin{simple_couplet}[#1]}
}
}
{
\bool_if:NTF\g_my_song_bool
{\end{couplet_chant}}
{\end{simple_couplet}}
}

\NewDocumentCommand{\longest}{m}{\settowidth{\g_my_linewidth_dim}{#1}}


\NewDocumentEnvironment{song}{mo}
% #1: number of stanzas per slice (1 or 2)
% #2: list of verses to include (defaults to all)
{
% Clear out refrain and couplets
\tl_gclear:N\g_my_refrain_tl
\seq_gclear:N\g_my_couplets_seq
% And longest line width, which will normally be defined inside the song
\dim_zero:N{\g_my_linewidth_dim}

% Put arguments into variables with understandable names
\int_gset_eq:NN{\g_my_stanzas_per_slide_int}{#1}

\IfNoValueTF{#2}
{\seq_gclear:N\g_my_couplet_indexes_seq}
{\seq_gset_from_clist:Nn\g_my_couplet_indexes_seq{#2}}

% We are now in a song, and at its start
\bool_gset_true:N\g_my_song_bool
\bool_gset_true:N\g_my_song_start_bool
}
{
% Type the refrain
\tl_gset:Nn\g_my_song_refrain
{
\dim_compare:nNnTF{\g_my_linewidth_dim}{=}{0pt}
{\begin{simple_refrain}}
{\begin{simple_refrain}[\g_my_linewidth_dim]}
\tl_use:N\g_my_refrain_tl
\end{simple_refrain}
}

% Type the n-th couplet (with an \onslide before)
\cs_gset:Npn\g_my_song_couplet:n##1
{
\onslide<+>
\dim_compare:nNnTF{\g_my_linewidth_dim}{=}{0pt}
{\begin{simple_couplet}}
{\begin{simple_couplet}[\g_my_linewidth_dim]}
\seq_item:Nn\g_my_couplets_seq{##1}
\end{simple_couplet}
}

% Type the n-th couplet followed by the refrain (with an \onslide before each)
\cs_gset:Npn\g_my_song_couplet_refrain:n##1
{
\g_my_song_couplet:n{##1}
\onslide<+>
\g_my_song_refrain
}

% Écrit tous les couplets (dans un {overprint})
\tl_gset:Nn\g_my_song_couplets
{
\begin{overprint}
\seq_if_empty:NTF{\g_my_couplet_indexes_seq}
{\int_step_function:nN{\seq_count:N\g_my_couplets_seq}\g_my_song_couplet:n}
{\seq_map_function:NN\g_my_couplet_indexes_seq\g_my_song_couplet:n}
\end{overprint}
}

% Type refrain and all verses (in an {overprint})
\tl_gset:Nn\g_my_song
{
\begin{overprint}
\bool_if:NTF\g_my_refrain_first_bool
{
\onslide<+>
\g_my_song_refrain
} {}
\seq_if_empty:NTF{\g_my_couplet_indexes_seq}
{\int_step_function:nN{\seq_count:N\g_my_couplets_seq}\g_my_song_couplet_refrain:n}
{\seq_map_function:NN\g_my_couplet_indexes_seq\g_my_song_couplet_refrain:n}
\end{overprint}
}

\int_compare:nNnTF{\g_my_stanzas_per_slide_int}{>}{1}
{
\bool_if:NTF\g_my_refrain_first_bool
{
\vfill \vfill
\g_my_song_refrain
\vfill
\g_my_song_couplets
\vfill \vfill
}
{
\vfill \vfill
\g_my_song_couplets
\vfill
\g_my_song_refrain
\vfill \vfill
}
}
{\g_my_song}
\bool_gset_false:N\g_my_song_bool
}

\ExplSyntaxOff
```

--
. o .
. . o Tanguy
o o o
Reply all
Reply to author
Forward
0 new messages