diff options
Diffstat (limited to 'macros/latex/contrib/recorder-fingering/recorder-fingering.sty')
-rw-r--r-- | macros/latex/contrib/recorder-fingering/recorder-fingering.sty | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/macros/latex/contrib/recorder-fingering/recorder-fingering.sty b/macros/latex/contrib/recorder-fingering/recorder-fingering.sty new file mode 100644 index 0000000000..6c5f447f72 --- /dev/null +++ b/macros/latex/contrib/recorder-fingering/recorder-fingering.sty @@ -0,0 +1,355 @@ +%% +%% This is file `recorder-fingerings.sty' +% Copyright 2023 by Alan Munn +% +% This package provides a single command to produce a fingering +% diagram for any not in the playable range of the recorder. +% +% This package may be distributed and/or modified under the +% conditions of the LaTeX Project Public License, either version 1.3 +% of this license or any later version. +% The latest version of this license is in +% http://www.latex-project.org/lppl.txt +% and version 1.3 or later is part of all distributions of LaTeX +% version 2005/12/01 or later. +% +% This package has the LPPL maintenance status `maintained'. +% +% The Current Maintainer of this package is Alan Munn. +% +% This package consists of the file +% +% recorder-fingering.sty +% +% and documentation files: +% +% recorder-fingering.tex, recorder-fingering.pdf +% +% Version 1.0 2023/02/17 +\NeedsTeXFormat{LaTeX2e}[2022/11/01] +\ProvidesPackage{recorder-fingering}[2023/02/17 v1.0 Recorder fingering diagrams] +\RequirePackage{tikz} +\usetikzlibrary{calc} +\RequirePackage{graphicx} +% Split node code from +% https://tex.stackexchange.com/a/657282/2693 +% These styles are local to the package +\pgfkeys{/fingering/.style={ + node split radius/.initial=1, + node split color 1/.initial=white, + node split color 2/.initial=black, + node split color 3/.initial=blue, + node split half/.style={node split={##1,##1+180}}, + node split/.style args={##1,##2}{ + path picture={ + \tikzset{ + x=($(path picture bounding box.east)-(path picture bounding box.center)$), + y=($(path picture bounding box.north)-(path picture bounding box.center)$), + radius=\pgfkeysvalueof{/tikz/node split radius}} + \foreach \ang[count=\iAng, remember=\ang as \prevAng (initially ##1)] in {##2,360+##1} + \fill[line join=round, draw, fill=\pgfkeysvalueof{/tikz/node split color \iAng}] + (path picture bounding box.center) + --++(\prevAng:\pgfkeysvalueof{/tikz/node split radius}) + arc[start angle=\prevAng, end angle=\ang] --cycle; +} }, +hole/.style={circle,draw,##1}, +1/.style={fill=black}, % closed hole +0/.style={fill=none}, % open hole +t/.style={node split half=0}, % thumb half hole +h/.style={node split half=-90}, % finger half hole +top double/.style={circle,minimum size=7pt,inner sep=0pt,draw,##1}, +bottom double/.style={circle,minimum size=6pt,inner sep=0pt,draw,##1}, +}} +\ExplSyntaxOn + + +% Error messages +\msg_new:nnnn {recorder-fingering}{too-few}{\\You\ haven't\ provided\ enough\ holes!}{Fingerings\ must\ specify\ all\ 8\ holes\ (thumb\ +\ all\ 7\ fingers)} +\msg_new:nnnn {recorder-fingering}{key-exists}{\\This\ fingering\ is\ already\ defined!}{Use\ the\ starred\ version\ of\ the\ command\ or\ \token_to_str:N \AddFingerings\ instead\ to\ override\ existing\ fingerings.} +\msg_new:nnnn {recorder-fingering}{wrong-instrument}{\\This\ command\ only\ permits\ F\ and\ C\ fingerings!}{} +\msg_new:nnnn {recorder-fingering}{deprecated}{\\This\ command\ is\ deprecated.\ Please\ use\ \token_to_str:N \alto\ or\ \token_to_str:N \soprano\ instead.}{} + +% Command parameters +\keys_define:nn {fingering}{ + thumboffset.bool_set:N = \g_fingering_thumboffset_bool, + width.dim_set:N = \g_fingering_size_dim, + thumboffset.initial:n = {true}, + width.initial:n = {20pt} +} +\keys_define:nn {fingering / command}{ + thumboffset.bool_set:N = \g_fingering_command_thumboffset_bool, + width.dim_set:N = \l_fingering_command_size_dim, +} +\NewDocumentCommand{\fingeringSetup}{m}{ + \keys_set:nn {fingering} {#1} +} + +% Manage the thumb offset parameters +\tl_set:Nn \g_fingering_thumboffset_x {-.5} +\tl_set:Nn \g_fingering_thumboffset_y {-.25} +\cs_new:Nn \fingering_offset_reset: +{\tl_gset:Nn \g_fingering_thumboffset_x {-.5} +\tl_gset:Nn \g_fingering_thumboffset_y {-.25} +} +\cs_new:Nn \fingering_offset_zero: +{\tl_gset:Nn \g_fingering_thumboffset_x {0} +\tl_gset:Nn \g_fingering_thumboffset_y {0} +} +\cs_new:Nn \fingering_set_thumboffset: { +\bool_if:nTF {\g_fingering_command_thumboffset_bool} + {\fingering_offset_reset:} + {\fingering_offset_zero:} +} + +% Set up the property lists for F and C fingerings +\prop_new:N \l_fingering_F_prop +\prop_new:N \l_fingering_C_prop +% Set up transposition tables for later +\prop_new:N \l_fingering_C_to_F_prop +\prop_new:N \l_fingering_F_to_C_prop + +% Set up the clists for the fingering vectors themselves +\clist_new:N \l_fingering_fing_clist +\clist_new:N \l_fingering_topholes_clist + + +% Generic fingering generator: takes a clist, splits it between top holes and bottom holes +% and produces the corresponding diagram +% basic idea for splitting the clist using step_inline and pop from Jonathan P. Spratte (Skillmon) + +\cs_new_protected:Nn \fingering_generate_fingering:n +{ +\clist_set:Nn \l_fingering_holes_clist {#1} + \clist_clear:N \l_fingering_topholes_clist + \int_step_inline:nn { 6 } { + \clist_pop:NNF \l_fingering_holes_clist \l_tmpa_tl { \msg_error:nn { recorder-fingering } { too-few }} + \clist_put_right:NV \l_fingering_topholes_clist \l_tmpa_tl } + \resizebox{\l_fingering_command_size_dim}{!}{ + \begin{tikzpicture}[/fingering] +% display the topholes + \fingering_set_thumboffset: + \foreach[expand~list]~\x~[count=\xi]~in~\l_fingering_topholes_clist { + \node[hole=\x]~at~(0+\g_fingering_thumboffset_x,-\xi/2+\g_fingering_thumboffset_y)~{}; + \fingering_offset_zero:} +% display the bottom two paired holes + \clist_pop:NN \l_fingering_holes_clist \l_tmpa_tl + \int_case:nnTF {\l_tmpa_tl}{ + {0} {\node[top~double=0]~at~(.15,-7/2)~{}; + \node[bottom~double=0]~at~(-.15,-7.15/2)~{};} + {2} {\node[top~double=1,]~at~(.15,-7/2)~{}; + \node[bottom~double=1]~at~(-.15,-7.15/2)~{};} + {1} {\node[top~double=1]~at~(.15,-7/2)~{}; + \node[bottom~double=0]~at~(-.15,-7.15/2)~{};} + } + {} + {} + \clist_pop:NN \l_fingering_holes_clist \l_tmpa_tl + \int_case:nnTF {\l_tmpa_tl}{ + {0} {\node[top~double=0,]~at~(.15,-8/2)~{}; + \node[bottom~double=0]~at~(-.15,-8.15/2)~{};} + {2} {\node[top~double=1,]~at~(.15,-8/2)~{}; + \node[bottom~double=1]~at~(-.15,-8.15/2)~{};} + {1} {\node[top~double=1,]~at~(.15,-8/2)~{}; + \node[bottom~double=0]~at~(-.15,-8.15/2)~{};} + } + {} + {} +% display the bell hole if present + \clist_if_empty:NTF \l_fingering_holes_clist + {} + {\clist_pop:NN \l_fingering_holes_clist \l_tmpa_tl + \node[hole=\l_tmpa_tl]~at~(0,-9/2)~{};} + \end{tikzpicture}} + } + +% create variable version of the command +\cs_generate_variant:Nn \fingering_generate_fingering:n { V } + + +% Main user commands and synonyms +% First basic commands to display the fingering for any note +\NewDocumentCommand{\Alto}{om}{ +\IfNoValueTF{#1} + {\keys_set:nn {fingering / command} {width = \g_fingering_size_dim} + \bool_set_eq:NN {\g_fingering_command_thumboffset_bool}{\g_fingering_thumboffset_bool}} + {\bool_set_eq:NN {\g_fingering_command_thumboffset_bool}{\g_fingering_thumboffset_bool} + \keys_set:nn {fingering /command } {#1}} + \prop_get:NnN \l_fingering_F_prop {#2} \l_tmpa_tl + \fingering_generate_fingering:V \l_tmpa_tl +} +\cs_set_eq:NN \Sopranino \Alto +\cs_set_eq:NN \Bass \Alto + +\NewDocumentCommand{\Soprano}{om}{ +\IfNoValueTF{#1} + {\keys_set:nn {fingering / command} {width = \g_fingering_size_dim} + \bool_set_eq:NN {\g_fingering_command_thumboffset_bool}{\g_fingering_thumboffset_bool}} + {\bool_set_eq:NN {\g_fingering_command_thumboffset_bool}{\g_fingering_thumboffset_bool} + \keys_set:nn {fingering /command } {#1}} + \prop_get:NnN \l_fingering_C_prop {#2} \l_tmpa_tl + \fingering_generate_fingering:V \l_tmpa_tl +} +\cs_set_eq:NN \Tenor \Soprano + +% Now commands to add new fingerings: +% First, a note = { } version for multiple fingerings +% This command will override existing fingerings +\NewDocumentCommand{\AddFingerings}{mm}{ + \str_case:nnTF {#1}{ + {F} {\prop_put_from_keyval:Nn \l_fingering_F_prop {#2}} + {C}{\prop_put_from_keyval:Nn \l_fingering_C_prop {#2}} + } + {} + {\msg_error:nn { recorder-fingering }{ wrong-instrument}} +} + +% Second individual fingerings for alto and soprano +% These commands will throw an error if the note is already defined +\NewDocumentCommand{\NewFfingering}{smm}{ +\IfBooleanTF{#1} + {\prop_put:Nnn \l_fingering_F_prop {#2} {#3}} + {\prop_get:NnNTF \l_fingering_F_prop {#2} \tl_tmpa_tl + {\msg_error:nn { recorder-fingering } { key-exists }} + {\prop_put:Nnn \l_fingering_F_prop {#2} {#3}} + }} + +\NewDocumentCommand{\NewCfingering}{smm}{ +\IfBooleanTF{#1} + {\prop_put:Nnn \l_fingering_C_prop {#2} {#3}} + {\prop_get:NnNTF \l_fingering_C_prop {#2} \tl_tmpa_tl + {\msg_error:nn { recorder-fingering } { key-exists }} + {\prop_put:Nnn \l_fingering_C_prop {#2} {#3}} + }} + +% Now add all of the basic fingerings +\AddFingerings{F}{ + F = {1,1,1,1,1,1,2,2}, + Gb = {1,1,1,1,1,1,2,1}, + G = {1,1,1,1,1,1,2,0}, + Ab = {1,1,1,1,1,1,0,0}, + A = {1,1,1,1,1,1,0,0}, + Bb = {1,1,1,1,1,0,2,2}, + B = {1,1,1,1,0,1,2,0}, + C = {1,1,1,1,0,0,0,0}, + Db = {1,1,1,0,1,1,1,0}, + D = {1,1,1,0,0,0,0,0}, + Eb = {1,1,0,1,1,0,0,0}, + E = {1,1,0,0,0,0,0,0}, + f = {1,0,1,0,0,0,0,0}, + gb = {0,1,1,0,0,0,0,0}, + g = {0,0,1,0,0,0,0,0}, + ab = {0,0,1,1,1,1,2,0}, + a = {t,1,1,1,1,1,0,0}, + bb = {t,1,1,1,1,0,2,0}, + b = {t,1,1,1,0,1,0,0}, + c = {t,1,1,1,0,0,0,0}, + db = {t,1,1,0,1,0,0,0}, + d = {t,1,1,0,0,0,0,0}, + eb = {t,1,1,0,0,1,2,1}, + e = {t,1,1,0,1,1,0,0}, + f' = {t,1,0,0,1,1,0,0}, + gb' = {t,1,0,1,1,0,2,2,1}, + g' = {t,1,0,1,1,0,2,2}, + ab' = {t,0,1,1,0,1,2,0} + } +\AddFingerings{C}{ + C = {1,1,1,1,1,1,2,2}, + Db = {1,1,1,1,1,1,2,1}, + D = {1,1,1,1,1,1,2,0}, + Eb = {1,1,1,1,1,1,1,0}, + E = {1,1,1,1,1,1,0,0}, + F = {1,1,1,1,1,0,2,2}, + Gb = {1,1,1,1,0,1,1,0}, + G = {1,1,1,1,0,0,0,0}, + Ab = {1,1,1,0,1,1,0,0}, + A = {1,1,1,0,0,0,0,0}, + Bb = {1,1,0,1,1,0,0,0}, + B = {1,1,0,0,0,0,0,0}, + c = {1,0,1,0,0,0,0,0}, + db = {0,1,1,0,0,0,0,0}, + d = {0,0,1,0,0,0,0,0}, + eb = {0,0,1,1,1,1,2,0}, + e = {t,1,1,1,1,1,0,0}, + f = {t,1,1,1,1,0,2,0}, + gb = {t,1,1,1,0,1,0,0}, + g = {t,1,1,1,0,0,0,0}, + ab = {t,1,1,0,1,0,0,0}, + a = {t,1,1,0,0,0,0,0}, + bb = {t,1,1,0,0,1,2,1}, + b = {t,1,1,0,1,1,0,0}, + c' = {t,1,0,0,1,1,0,0}, + db' = {t,1,0,1,1,0,2,2,1}, + d' = {t,1,0,1,1,0,2,2}, + eb' = {t,0,1,1,0,1,2,0} +} + +% Transposition tables (may be useful; currently not used) +\prop_set_from_keyval:Nn \l_fingering_F_to_C_prop { + F = {C}, + Gb = {Db}, + G = {D}, + Ab = {Eb}, + A = {E}, + Bb = {F}, + B = {Gb}, + C = {G}, + Db = {Ab}, + D = {A}, + Eb = {Bb}, + E = {B}, + f = {c}, + gb = {db}, + g = {d}, + ab = {eb}, + a = {e}, + bb = {f}, + b = {gb}, + c = {g}, + db = {ab}, + d = {a}, + eb = {bb}, + e = {b}, + f' = {c'}, + gb' = {db'}, + g' = {d'}, + ab' = {eb'} + } + +\prop_set_from_keyval:Nn \l_fingering_C_to_F_prop { + C = {F}, + Db = {Gb}, + D = {G}, + Eb = {Ab}, + E = {A}, + F = {Bb}, + Gb = {B}, + G = {C}, + Ab = {Db}, + A = {D}, + Bb = {Eb}, + B = {E}, + c = {f}, + db = {gb}, + d = {g}, + eb = {ab}, + e = {a}, + f = {bb}, + gb = {b}, + g = {c}, + ab = {db}, + a = {d}, + bb = {eb}, + b = {e}, + c' = {f'}, + db' = {gb'}, + d' = {g'}, + eb' = {ab'} + } + + +% Backwards (in)compatibility with the initial version of the package + +\NewDocumentCommand{\fingering}{}{ + \msg_error:nn {recorder-fingering}{deprecated}} +\ExplSyntaxOff +\endinput |