dass ich so weit weg von der Lösung war, dachte ich nicht :-)
Warst Du doch gar nicht

Wieso geht es nicht, dieselbe Funktion mit cdr aufzurufen?
Zunächst, Stefan hatte es mit einer (do ...)-Schleife versucht, Du mit einer Rekursion.
Ich fand das alles zu kompiliziert und unnötig, deshalb mein einfaches (map ...)
Aber natürlich geht es!
Hier Deine Rekursion mit Anmerkungen:
\version "2.18.2"
Noten = { c'4 d' e' f' }
TextOne = { \lyricmode { ers -- te Stro -- phe } }
TextTwo = { \lyricmode { zwei -- te Stro -- phe } }
TextThree = { \lyricmode { drit -- te Stro -- phe } }
addText = #(define-music-function (parser location voice text)(string? list?)
;; Die Abbruchbedingung ist zwar ok, aber was wird denn ausgegeben? Nur: #{ #}
;; Aber:
;; #(display-scheme-music #{ #})
;; -> (make-music (quote Music))
;; Also im wesentlichen also gar nix 
(if (null? text)
#{ #}
;; Der folgende Ausdruck ist auch nicht zielführend da ein geschachtelter Ausdruck entsteht:
;;
;; (make-music
;; 'SequentialMusic
;; 'elements
;; ;; first Lyrics
;; (list (make-music
;; 'ContextSpeccedMusic
;; 'create-new
;; #t
;; ...)
;; ;; second Lyrics
;; (make-music
;; 'SequentialMusic
;; 'elements
;; (list (make-music
;; 'ContextSpeccedMusic
;; 'create-new
;; #t
;; ...)))
;; ;; third Lyrics
;; (make-music
;; 'SequentialMusic
;; 'elements
;; (list (make-music
;; 'ContextSpeccedMusic
;; 'create-new
;; #t
;; ...)))
;; (make-music (quote Music))))))))
;; dieser müßte erst durch die music-function ausgegeben, entfaltet und in sinnvoller Weise in den Score integriert werden.
;; Letzteres funktioniert, falls man es als simultaneous-music oder innerhalb von << >> ausgibt.
#{
\new Lyrics \lyricsto $voice $(car text)
\addText $voice $(cdr text)
#}
)
)
\score {
<<
\new Staff { \new Voice = "music" { \Noten }}
\addText #"music" #(list TextOne TextTwo TextThree)
>>
}
Obwohl ich wie gesagt die Anwendung des einfachen (map ...) hier bevorzuge, habe ich Deinen Ansatz mal überarbeitet. Das führt zu:
\version "2.18.2"
Noten = { c'4 d' e' f' }
TextOne = \lyricmode { ers -- te Stro -- phe }
TextTwo = \lyricmode { zwei -- te Stro -- phe }
TextThree = \lyricmode { drit -- te Stro -- phe }
addText =
#(define-music-function (parser location rl voice text)
(list? string? list?)
(if (null? text)
(make-simultaneous-music (reverse rl))
#{
\addText
$(cons #{ \new Lyrics \lyricsto $voice $(car text) #} rl)
$voice
$(cdr text)
#}))
\score {
<<
\new Staff { \new Voice = "music" { \Noten }}
\addText #'() "music" #(list TextOne TextTwo TextThree)
>>
}
EDIT: define-scheme-function zu define-music-function geändert (Tatsächlich funktioniert beides, es gibt aber keinen besonderen Grund define-scheme-function zu benutzen.)
Du siehst ein Argument mehr ,`rl', welches später als '() gesetzt wird, eine leere Liste in der alle Lyrics-contexte akkumuliert und dann via make-simultaneous-music ausgegeben werden.
Es entsteht auch keine geschachtelte Liste, da `cons' verwendet ist.
In meiner eigenen Funktion habe ich noch ein bißchen mehr Aufwand betrieben um eine möglichst LilyPond-artige Eingabe zu ermöglichen.
Wie aus meinen angefügten Beispielen hervorgeht kann man \TextOne oder { \TextOne } für einen einzelnen Lyrics-context benutzen oder z.B. { \TextOne \TextTwo \TextThree } für mehrere.
HTH,
Harm