Using TikZ to depict intonation

Following up on Matthew Leingang's answer, here's the same approach tied up with some syntactic sugar. It messes around with \catcode stuff, so care needs to be taken.

\documentclass{standalone}
\usepackage{tikz}

\newcount\contourmarkcount
\newdimen\contourraise

{\catcode`\|=13
\gdef\installbarmark#1\ignorespaces{%
    #1\ignorespaces%
    \catcode`\|=13%
    \global\contourmarkcount=0\relax%
    \global\def\lastmarkshift{0}%
    \let|=\marktext}%
}


\def\star{*}
\newcommand\marktext[1][*]{%
    \def\markshift{#1}%
    % When not followed by the optional argument
    % the contour mark is set at the previous height.
    \ifx\markshift\star
        \let\markshift=\lastmarkshift%
    \fi
    \global\advance\contourmarkcount by1\relax%
    \xdef\tmpmark{\the\contourmarkcount}%
    \tikz[remember picture, overlay, y=\pgfkeysvalueof{/tikz/contour scale}]
        \path [yshift=\contourraise, shift={(0,\markshift)}]
                coordinate (\contourmarkprefix-\tmpmark);%
    \global\contourmarkcount=\tmpmark\relax% 
    \global\let\lastmarkshift=\markshift%
}


\tikzset{
    intonation contour/.style={%
        execute at begin node={%
            \installbarmark%
        },
        append after command={%
            \pgfextra{%
                \ifnum\contourmarkcount>1
                    \draw [contour] (\contourmarkprefix-1)
                        \foreach \y in {2,...,\the\contourmarkcount}{ -- (\contourmarkprefix-\y) };
                \fi
            }
        }
    },
    % How far above the base line of the text,
    raise contour/.code=\pgfmathsetlength\contourraise{#1},
    % The `scale' for the values in the contour height specification
    contour scale/.initial=3pt,
    % The prefix for the contour marks.
    contour mark prefix/.code=\xdef\contourmarkprefix{#1},
    contour mark prefix=intonation contour,
    contour/.style={
        draw, 
        rounded corners=1ex,
    }           
}

\begin{document}

\begin{tikzpicture}[remember picture]

\node [intonation contour, raise contour=0.5cm] 
    {|[10]Where |[3]are |[3]you |[5]go|[2]ing|[0]?};

\end{tikzpicture}

\begin{tikzpicture}[remember picture]

\node [intonation contour, raise contour=0.5cm, contour mark prefix=my contour] 
    {|[2]I  |am  |[7]fina|lly |[4]go|ing |[2]home|[0].};

\path [draw=red, ->] ([yshift=0.25cm]my contour-2) -- ([yshift=0.25cm]my contour-3)
        node [midway, left] {\tiny rising};

\path [draw=red, ->] ([yshift=0.25cm]my contour-4) -- ([yshift=0.25cm]my contour-5)
    node [midway, right] {\tiny falling};
\end{tikzpicture}

\end{document}

enter image description here

And just for fun, here's a version that doesn't use remember picture so doesn't need two compilations. Also there is no fooling around with category codes. It is, unfortunately, a bit more involved, and the letters are not typeset ideally as they are all in separate boxes.

EDIT A bit more versatility has been implemented, but as a result it is a bit more complex.

\documentclass{standalone}
\usepackage{tikz}

\newdimen\contourraise

\tikzset{
    % How far above the base line of the text,
    raise contour/.code=\pgfmathsetlength\contourraise{#1},
    % The `scale' for the values in the contour height specification
    contour scale/.initial=3pt,
    % The prefix for the contour marks.
    contour mark prefix/.code=\xdef\contourmarkprefix{#1},
    contour mark prefix=contour,
    % The style for the contour path
    contour/.style={
        draw, 
        rounded corners=1ex,
    },
    % The style for the token nodes
    contour token/.style={
        anchor=base west, 
        inner sep=0pt,
        text depth=0.6ex, % controls underline depth
    },
    contour underline/.style={
        draw
    },
    % The character to insert a mark (use with care)
    contour mark character/.store in=\contourmarkchar,
    contour mark character=|
}


\makeatletter

\def\at@{@}

\newcount\lasttokennumber
\newcount\currenttokennumber
\newcount\contourmarkcount
\newcount\contourtokenunderlinestate
\newcommand\contour[2][]{%
    \begin{scope}[#1]
        \coordinate (token-0);
        \currenttokennumber=0\relax%
        \lasttokennumber=0\relax%
        \contourmarkcount=0\relax%
        \def\lastcontourheight{0}%
        \contourtokenunderlinestate=0\relax%
        \@contour#2@%
}


% Must check for a spaces
\def\@contour{\futurelet\@token\@checkforspace}

\def\@uscore{_}
\def\@checkforspace{%
    \ifx\@token\@sptoken%
        \let\@next=\@replacespace%
    \else%
        \if\@token\contourmarkchar%
            \let\@next=\@contour@insertmark
        \else%
            \if\@token\@uscore
                \let\@next=\@contourtoggleunderline%
            \else%
                \let\@next=\@@contour%
            \fi%
        \fi%
    \fi%
    \@next%
}

\def\@contourtoggleunderline#1{%
    \advance\contourtokenunderlinestate by1\relax
    \ifnum\contourtokenunderlinestate>3\relax%
        \contourtokenunderlinestate=0\relax%
    \fi%
    \@contour%
}

\def\@contour@insertmark{%
    \afterassignment\@@contour@insertmark\let\@token=%
}

\def\@@contour@insertmark{%
    \futurelet\@token\@@@contour@insertmark}%

\def\@@@contour@insertmark{%
    \if\@token[%
        \let\@next=\@@@@contour@insertmark%
    \else%
        \let\currentcontourheight=\lastcontourheight%
        \let\@next=\@@@@@contour@insertmark%
    \fi%
    \@next%
}

\def\@@@@contour@insertmark[#1]{%
    \def\@tmp{#1}%
    \ifx\@tmp\@empty%
        \let\currentcontourheight=\lastcontourheight%
    \else%
        \def\currentcontourheight{#1}%
    \fi%
    \@@@@@contour@insertmark}

\def\@@@@@contour@insertmark{%
    \advance\contourmarkcount by1\relax%
    % Code for inserting mark
    \coordinate (\contourmarkprefix-\the\contourmarkcount)
        at ([yshift=\contourraise, y=\pgfkeysvalueof{/tikz/contour scale}, 
        shift={(0,\currentcontourheight)}]token-\the\currenttokennumber.base east);
    %
    \let\lastcontourheight=\currentcontourheight
\@contour}

\def\customspace{{\hbox to 1ex{\hfill}}}

\def\@replacespace#1{%
    \@contour\customspace#1%
}

\def\@@contour#1{%
    \def\@token{#1}%
    \if\@token\at@
        \let\@next=\@@@contour%
    \else%
        \lasttokennumber=\currenttokennumber%
        \advance\currenttokennumber by1%
        % Code for typesetting token
        \node [contour token/.try] at (token-\the\lasttokennumber.base east) (token-\the\currenttokennumber) {\@token};
        % Manage underline state
        \ifnum\contourtokenunderlinestate=1\relax%
            \coordinate (underline start) at (token-\the\currenttokennumber.south west);
            \contourtokenunderlinestate=2\relax%
        \else
            \ifnum\contourtokenunderlinestate=3\relax%
                \coordinate (underline end) at (token-\the\currenttokennumber.south west);
                \draw (underline start) -- (underline end);
                \contourtokenunderlinestate=0\relax
            \fi%
        \fi%
        \let\@next=\@contour
        %
    \fi%
    \@next%
}
\def\@@@contour{%
    \ifnum\contourmarkcount>1
        % Code for drawing contour
        \draw [contour] (\contourmarkprefix-1)
            \foreach \y in {2,...,\the\contourmarkcount}{ -- (\contourmarkprefix-\y) };
        %
    \fi%
    \end{scope}%
}

\makeatother

\begin{document}

\begin{tikzpicture}[baseline={(0,-0.25)}]

    \contour[raise contour=0.5cm]
        {|[10]Where |[3]are |[3]_you_ |[5]go|[2]ing|[0]?}

\end{tikzpicture} 


\begin{tikzpicture}[baseline={(0,-0.25)}]

\contour[
    raise contour=0.5cm, 
    contour mark prefix=my contour, 
    contour/.style={
        thick, 
        rounded corners=1mm,
        line cap=round,
        dotted},
    contour mark character=*] 
    {*[2]I *am  *[7]_{fi}na*lly_ *[4]go*ing *[2]home*[0].};

\path [draw=red, ->] ([yshift=0.25cm]my contour-2) -- ([yshift=0.25cm]my contour-3)
        node [midway, left] {\tiny rising};

\path [draw=red, ->] ([yshift=0.25cm]my contour-4) -- ([yshift=0.25cm]my contour-5)
    node [midway, right] {\tiny falling};

\path (0,-0.25);
\end{tikzpicture}

\end{document}

enter image description here

EDIT: enhanced version which hopefully covers all the use cases given above.

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{fit}

\newdimen\contourraise
\newdimen\contourspacetokenwidth
\newcount\lasttokennumber
\newcount\currenttokennumber
\newcount\contourmarkcount
\newcount\contourtokenunderlinestate
\newbox\contourbox

\tikzset{
    tight fit/.style={
        inner sep=0pt,
        outer sep=0pt,
    },
    %
    %
    % How far above the reference anchor of the text,
    contour raise/.code=\pgfmathsetlength\contourraise{#1},
    contour reference anchor/.store in=\contourreferenceanchor,
    contour reference anchor=base east,
    % The `scale' for the values in the contour height specification
    contour scale/.store in=\contourscale,
    contour scale=3pt,
    % The prefix for the contour marks.
    contour mark prefix/.store in=\contourmarkprefix,
    contour mark prefix=contour,
    % The style for the contour path
    contour/.style={
        draw, 
        rounded corners=1ex,
    },
    % The style for the token nodes
    every contour token/.style={
        anchor=base west, 
        inner sep=0pt,
    },
    contour underline/.style={
        draw
    },
    % The character to insert a mark (use with care)
    contour mark character/.store in=\contourmarkchar,
    contour mark character=|,
    % Want to change the code for contour marks? Use this key.
    contour mark code/.store in=\contourmarkcode,
    % Want to change the code for tokens? Use this key.
    contour token code/.store in=\contourtokencode,
    % Want to change the code for drawing the contour? Use this  key.
    contour code/.store in=\contourcode,
    %
    % Default stuff
    contour mark code={%
        \coordinate (\contourmarkprefix-\the\contourmarkcount)
          at ([yshift=\contourraise, y=\contourscale,               
          shift={(0,\currentcontourheight)}]token-\the\currenttokennumber.\contourreferenceanchor);
    },
    contour token code={%
        \node [every contour token/.try] at 
        (token-\the\lasttokennumber.base east) 
            (token-\the\currenttokennumber) {\token};
    },
    contour code={
        \draw [contour] (\contourmarkprefix-1)
            \foreach \y in {2,...,\the\contourmarkcount}{ -- 
                    (\contourmarkprefix-\y) };                  
    },
    %
    % Don't draw the contour.
    tokens only/.style={
        contour code={}
    },
    %
    % Only draw the contour (but the space is still used for the tokens)
    contour only/.style={
        every contour token/.append style={
            execute at begin node={\setbox\contourbox=\hbox\bgroup},
            execute at end node=\egroup\phantom{\box\contourbox}%
        },
        underline/.style={
            draw=none
        }
    },
    %
    % Make tokens follow the contour marks.
    tokens follow contour/.style={
        tokens only,
        contour token code={%
            \node [every contour token/.try, y=\contourscale] at 
                (token-\the\lasttokennumber.base east |- 
                0,\currentcontourheight) 
                (token-\the\currenttokennumber) {\token};
        },
    },
    % What style to use when drawing underline
    underline/.style={
        draw
    },
    % The underline is drawn along the south side of a node which 
    % takes this style.
    underline token/.style={
        inner ysep=1pt
    },
    % When grouping tokens (e.g., for putting box around)
    % this style is applied to a node that is fitted around the group
    token group/.style={
        inner xsep=1pt,
        inner ysep=2pt,
        rounded corners=2pt
    },
    % Draw boxes around tokens groups.
    box tokens/.style={
        token group/.append style={
            draw
        }
    },  
    % Change the width of the spaces.
    space token width/.code=\pgfmathsetlength\contourspacetokenwidth{#1},
    space token width=0.125cm
}


\makeatletter

\def\at@{@}


\newcommand\contour[2][]{%
    \begin{scope}[#1]
        \coordinate (token-0);
        \currenttokennumber=0\relax%
        \lasttokennumber=0\relax%
        \contourmarkcount=0\relax%
        \def\lastcontourheight{0}%
        \contourtokenunderlinestate=0\relax%
        \@contour#2@%
}


% Must check for a spaces
\def\@contour{\futurelet\@token\@checkforspace}

\def\@uscore{_}
\def\@checkforspace{%
    \ifx\@token\@sptoken%
        \let\@next=\@replacespace%
    \else%
        \if\@token\contourmarkchar%
            \let\@next=\@contour@insertmark
        \else%
            \if\@token\@uscore
                \let\@next=\@contourtoggleunderline%
            \else%
                \let\@next=\@@contour%
            \fi%
        \fi%
    \fi%
    \@next%
}

\def\@contourtoggleunderline#1{%
    \advance\contourtokenunderlinestate by1\relax
    \ifnum\contourtokenunderlinestate>3\relax%
        \contourtokenunderlinestate=0\relax%
    \fi%
    \@contour%
}

\def\@contour@insertmark{%
    \afterassignment\@@contour@insertmark\let\@token=%
}

\def\@@contour@insertmark{%
    \futurelet\@token\@@@contour@insertmark}%

\def\@@@contour@insertmark{%
    \if\@token[%
        \let\@next=\@@@@contour@insertmark%
    \else%
        \let\currentcontourheight=\lastcontourheight%
        \let\@next=\@@@@@contour@insertmark%
    \fi%
    \@next%
}

\def\@@@@contour@insertmark[#1]{%
    \def\@tmp{#1}%
    \ifx\@tmp\@empty%
        \let\currentcontourheight=\lastcontourheight%
    \else%
        \def\currentcontourheight{#1}%
    \fi%
    \@@@@@contour@insertmark}

\def\@@@@@contour@insertmark{%
    \advance\contourmarkcount by1\relax%
     % Code for inserting mark
    \contourmarkcode%
    \let\lastcontourheight=\currentcontourheight%
    \@contour}

\def\contourspacetoken{{\hbox to \contourspacetokenwidth{\hfill}}}

\def\@replacespace#1{%
    \@contour\contourspacetoken#1%
}

\def\@@contour#1{%
    \def\@token{#1}%
    \if\@token\at@%
        \@contourdounderline%
        \pgfutil@ifundefined{pgf@sh@ns@tokengroup}{}{%
            \node [tight fit, fit={(tokengroup)}, token group/.try] {};
            \global\let\pgf@sh@ns@tokengroup=\relax%
        }%
        \let\@next=\@@@contour%
    \else%
        \lasttokennumber=\currenttokennumber%
        \advance\currenttokennumber by1%
        \let\token=\@token%
        % Code for typesetting token
        \contourtokencode%
        % Manage underline state
        \@contourdounderline%
        \def\@@token{\contourspacetoken}%
        \ifx\@token\@@token%
            \pgfutil@ifundefined{pgf@sh@ns@tokengroup}{}{%
                \pgfutil@ifundefined{pgf@sh@ns@underline}{}{%
                    \node [tight fit, fit={(tokengroup) (underline)}] 
                    (tokengroup) 
                {};}%
                \node [tight fit, fit={(tokengroup)}, token group/.try] {};
                \global\let\pgf@sh@ns@tokengroup=\relax%
            }%
        \else
            \pgfutil@ifundefined{pgf@sh@ns@tokengroup}{%
                \node [tight fit, 
                fit={(token-\the\currenttokennumber)}] 
                (tokengroup) {};
            }{%
                \node [tight fit, 
                fit={(token-\the\currenttokennumber) 
                (tokengroup)}] 
                (tokengroup){};
            }%
        \fi%
        \let\@next=\@contour
        %
    \fi%
    \@next%
}

\def\@contourdounderline{%
    \ifcase\contourtokenunderlinestate%
     \or
         \node [tight fit, fit={(token-\the\currenttokennumber)}] 
         (underline) {};
         \contourtokenunderlinestate=2\relax%
     \or%
            \node [tight fit,fit={(token-\the\currenttokennumber) (underline)}]
            (underline) {};
     \or%
            \node [tight fit, fit={(underline)}, underline token/.try] 
            (underline) {};
         \draw [underline/.try]
                    (underline.south west) -- (underline.south east);
            \pgfutil@ifundefined{pgf@sh@ns@tokengroup}{}{%
                 \node [tight fit, fit={(tokengroup) (underline)}] 
                 (tokengroup) {};%
                 \node [tight fit, fit={(tokengroup)}, token group/.try] {};
                 \global\let\pgf@sh@ns@tokengroup=\relax%
                 \global\let\pgf@sh@ns@underline=\relax%
             }
         \contourtokenunderlinestate=0\relax
     \fi%
}
\def\@@@contour{%
    \ifnum\contourmarkcount>1
        % Code for drawing contour
        \contourcode%
    \fi%
    \end{scope}%
}

\makeatother

\begin{document}

\begin{tabular}{c}
\\
\begin{tikzpicture}
    \contour[tokens follow contour]
        {|[10]_Where_ |[3]are you |[6]_go_|[1]ing?|[0]}
\end{tikzpicture} 

\\[0.5cm]

\begin{tikzpicture}
    \contour[tokens follow contour, box tokens, space token width=0.2cm]
       {|[10]_Where_ |[3]are you |[6]_go_ |[1]ing?|[0]}
\end{tikzpicture} 

\\[0.5cm]

\begin{tikzpicture}
    \contour[contour raise=0.5cm]
        {|[10]_Where_ |[3]are you |[6]_go_|[1]ing?|[0]}
\end{tikzpicture} 


\\[0.5cm]

\begin{tikzpicture}
    \contour[contour raise=0.5cm]
        {|[10]Where |[3]are |[3]_you_ |[5]go|[2]ing?|[0]}

    \contour[contour raise=0.5cm, contour only, contour/.append style={dashed}]
            {|[0]Where |[2]are |[8]you |[2]go|[2]ing?|[1]}

\end{tikzpicture} 

\\[1cm]

\begin{tikzpicture}
\contour[tokens follow contour,
    contour mark character=*] 
    {*[2]I *am  *[7]_{fi}na*lly_ *[4]go*ing *[2]_home_*[0]};
\end{tikzpicture} 
\\[0.5cm]
\begin{tikzpicture}
\contour[
    contour raise=0.5cm, 
    contour mark prefix=my contour,
    contour/.style={
        thick, 
        rounded corners=1mm,
        line cap=round,
        dotted},
    contour mark character=*] 
    {*[2]I *am  *[7]_{fi}na*lly_ *[4]go*ing *[2]home*[0].};

\path [draw=red, ->] ([yshift=0.25cm]my contour-2) -- ([yshift=0.25cm]my 
contour-3)
        node [midway, left] {\tiny rising};

\path [draw=red, ->] ([yshift=0.25cm]my contour-4) -- ([yshift=0.25cm]my 
contour-5)
    node [midway, right] {\tiny falling};

\end{tikzpicture}

\end{tabular}
\end{document}

enter image description here


I would use the famous \tikzmark macro and combine it with intersection coordinates. Here is the code:

\documentclass{standalone}
\usepackage{tikz}
\newcommand{\tikzmark}[1]{\tikz[overlay,remember picture,baseline] \node [anchor=base] (#1) {};}%
\tikzstyle{intonation}=[rounded corners=2mm,yshift=1.5ex]
\begin{document}

\begin{tikzpicture}[remember picture]
\node{\tikzmark{w}Where \tikzmark{a}are \tikzmark{y}you \tikzmark{g}go\tikzmark{i}ing\tikzmark{q}?};
\draw[intonation] (w |- 0,1) -- (a |- 0,0.2) -- (y |- 0,0.2) -- (g |- 0,0.6) -- (q |- 0,0);
\end{tikzpicture}

\end{document}

The \tikzmark macro saves the coordinates of the point of the document at which it is called. So the node text \tikzmark{w}Where \tikzmark{a}are... saves coordinates called w, a, etc., at various points in the sentence.

If A and B are TikZ nodes or coordinates, then (A |- B) is the intersection of a vertical line through A and a horizontal line through B. So (w |- 0,1) has as its x coordinate the baseline-left point of the w in where, and y coordinate 1. You can change this y coordinate as you wish for the other coordinate names.

Here is the result:

sample code output

You might be able to design a macro that specifies/marks the text and defines the y-values in the same argument. But this is a possible implementation.


I'm not sure I put the coordinates on the right places (and reused \zzb in two points) but something like

enter image description here

%!TEX TS-program = xelatex
%!TEX encoding = UTF-8 Unicode

\documentclass[a4paper,12pt, oneside]{article}

\usepackage{fontspec}
\defaultfontfeatures{Mapping=tex-text, Scale=MatchLowercase}
\setmainfont{Charis SIL}

\makeatletter
\def\savecoordinate#1#2{\sbox0{#2}\edef#1{\strip@pt\wd0}}
\makeatother

\usepackage{tikz}

\begin{document}


\savecoordinate\zza{Where }
\savecoordinate\zzb{Where are you}
\savecoordinate\zzc{Where are you going?}

\begin{table}[hbtp]
\begin{tabular}{l}
\tikz[x=1pt,y=1mm,rounded corners=2mm] \draw[very thick, gray](0,10)--(\zza,2)--(\zzb,2)--(\zzb,6)--(\zzc,0); \\
\mbox{Where are you going?}
\end{tabular}
\end{table}

\end{document}