A first approach would be to change the text of the tuplet number and append a small slur:
bow-up = \markup { \with-dimensions #'(0 . 3) #'(0 . 0)
\override #'(filled . #t)
\path #.1 #'((moveto 0 0)
(curveto 1 1.2 2 1.2 3 0)
(curveto 2 0.9 1 0.9 0 0)
(closepath)) }
bow-down = \markup { \with-dimensions #'(0 . 3) #'(0 . 0)
\override #'(filled . #t)
\path #.1 #'((moveto 0 0)
(curveto 1 -1.2 2 -1.2 3 0)
(curveto 2 -0.9 1 -0.9 0 0)
(closepath)) }
#(define-markup-command (tuplet-bow-up layout props tuplet-number) (markup?)
(interpret-markup layout props
(markup #:center-column(#:vspace -.2 bow-up #:vspace -.6 #:line(tuplet-number)))))
#(define-markup-command (tuplet-bow-down layout props tuplet-number) (markup?)
(interpret-markup layout props
(markup #:center-column(#:vspace .2 tuplet-number #:vspace -1 #:line(bow-down)))))
tupletBowUp = #(define-scheme-function (parser location tuplet-number) (string?)
#{\override TupletNumber.text = \markup \tuplet-bow-up #tuplet-number #})
tupletBowDown = #(define-scheme-function (parser location tuplet-number) (string?)
#{\override TupletNumber.text = \markup \tuplet-bow-down #tuplet-number #})
\score {
{
\clef bass
\time 3/4
\tupletBowUp "3" \tuplet 3/2 { c8 b, c } \tupletBowDown "3" \tuplet 3/2 { e8 g e } \tuplet 3/2 { e8 g e } c'4
}
\layout{}
}
With \tupletBowUp "3"
you set the number of all following tuplets to a 3 with a small bow above. With \tupletBowDown "3"
you set it to a 3 with a small bow below. You can put other numbers as you like.
Result:
But I am sure, this can also be achieved in a much more sophisticated manner. However, I don’t know the internals of LilyPond that well. For example, it would be great to have the small bow automatically switch above or below the number according to its position.
Edit: Another way would be to change the tuplet bracket like this proposal and one comment suggest (sorry, I only saw the comment after having edited my answer). I changed the code from the linked proposal a bit to simplify it and to make the slurs smaller:
printTupletBow = {
\override TupletBracket #'stencil = #ly:slur::print
\override TupletBracket #'thickness = #1.2
\override TupletBracket #'control-points =
#(lambda (grob)
(let* ((x-pos (ly:grob-property grob 'X-positions))
(pos (ly:grob-property grob 'positions))
(x-ln (interval-length x-pos))
(dir (ly:grob-property grob 'direction))
(height (- (cdr pos) (car pos)))
(height-corr (* 0.3 dir height))
(edge-height (ly:grob-property grob 'edge-height '(0.7 . 0.7))))
(list
(cons
(+ (car x-pos) (* x-ln 1/4))
(+ (car pos) (* 1 dir (car edge-height))))
(cons
(+ (car x-pos) (* x-ln 3/8))
(+ (car pos) (* dir (+ 1.5 height-corr))))
(cons
(+ (car x-pos) (* x-ln 5/8))
(+ (cdr pos) (* dir (- 1.5 height-corr))))
(cons
(+ (car x-pos) (* x-ln 3/4))
(+ (cdr pos) (* 1 dir (cdr edge-height)))))))
}
\score {
{
\clef bass
\time 3/4
\tuplet 3/2 { c8 b, c } \tuplet 3/2 { e8 g e } \tuplet 3/2 { e8 g e } c'4
}
}
\layout {
\printTupletBow
}
Result:
If you want to temporarily omit the bows, you can use \once \override TupletBracket.stencil = ##f
just before the relevant tuplet.
One problem with this approach is that the slur will be slanted quite a lot if the notes span bigger intervals. But it might be a nice solution if your piece includes mainly tuplets with beams.