I would make the code easier.
- Use the
chains library.
- Use the full power of nodes:
text * keys so that all nodes have the same size,
append after command helps to place the arrow and the little lines at the bottom.
- Use styles.
I also have used the node contents key that has been introduced with the CVS version (relatively easy to implement but very much helpful). Note that there is no text group for the node anymore ({<text>} is missing).
The \aemarkup needs a third (second mandatory) argument that represents the maximum number of lines/nodes. It is used to fill up the line if less then #3 loop elements are given. This was your hard-coded 14.
Code
\documentclass[varwidth]{standalone}
\usepackage{tikz} \usetikzlibrary{chains,arrows.meta}
\tikzset{
my node/.style 2 args={#1 style/.try, node contents={#2}},
x style/.style={text width=\widthof{M}, text depth=+0pt, text height=\heightof{M}+2pt,
align=center, inner xsep=+0.5pt, inner ysep=+1pt,
append after command={(\tikzlastnode.south) edge ++ (down:+1.5ex)}},
f style/.style={x style, text=gray},
o style/.style={x style, draw},
m style/.style={o style,
append after command={% the arrow from above
(\tikzlastnode.north) edge
[line width=+2pt, arrows={Stealth[length=+2ex,width=+1.5ex]}-] ++ (up:+3.5ex)}}}
\newcommand*\aemarkup[3][]{
\tikzpicture[start chain=going mid right,node distance=+.1em,#1]
\foreach \aeI[count=\cnt from 2,remember=\cnt] in {#2}% \cnt will be global after this
\node[on chain,my node/.expand once=\aeI];
\ifnum\cnt>#3\else
\foreach \aeI in {\cnt,...,#3}\node[on chain, my node={x}{}];
\fi
\endtikzpicture\par}
\begin{document}
Diagram:
\aemarkup{mx , ox , ox , ox , ox , ox , ox , oM , ox , ox , ox , oa , ox , ob , oc}{15}
\aemarkup{fx , ox , mx , ox , ox , ox , ox , oM , ox , ox , ox , oa , ox , ob , oc}{15}
\aemarkup{fx , ox , fx , ox , mx , ox , ox , oM , ox , ox , ox , oa , ox , ob , oc}{15}
\aemarkup{fx , ox , fx , ox , fx , ox , mx , oM , ox , ox , ox , oa , ox , ob , oc}{15}
\aemarkup{fx , ox , fx , ox , fx , ox , fx , oM , mx , ox , ox , oa , ox , ob , oc}{15}
\aemarkup{fx , ox , fx , ox , fx , ox , fx , oM , fx , ox , mx , oa , ox , ob , oc}{15}
\aemarkup{fx , ox , fx , ox , fx , ox , fx , oM , fx , ox , fx , oa , mx , ob , oc}{15}
\aemarkup{fx , ox , fx , ox , fx , ox , fx , oM , fx , ox , fx , oa , fx , ob , mc}{15}
\aemarkup{mx , ox , ox , oM , ox , oa , ob , xc}{15}
\aemarkup{fx , ox , mx , oM , ox , oa , ob , xc}{15}
\aemarkup{fx , ox , fx , oM , mx , oa , ob , xc}{15}
\aemarkup{fx , ox , fx , oM , fx , oa , mb , xc}{15}
\aemarkup{mx , oM , oa , xb , xc}{15}
\aemarkup{fx , oM , ma , xb , xc}{15}
\aemarkup{mM , xa , xb , xc}{15}
\end{document}
Output

Code (PGF)
For comparison, a quick and dirty implementation with only PGF (and pgffor).
The effect of the keys text width, text depth and text height (which are TikZ keys) are reproduced with a rule (a strut essentially) and LaTeX’s \makebox. The keys draw and text that sets the stroke and text color are implemented with rudimentary keys that set TeX macros. The same is true for the arrow.
The effect of the chains library is hard-coded with the \ifnum\cnt conditional and the \pgftransformshift.
TikZ really does a lot under the hood, doesn’t it?
\documentclass[varwidth]{standalone}
\usepackage{pgf,pgffor} \usepgflibrary{arrows.meta}
\pgfset{draw path/.code=\def\aeusepath{stroke},
none path/.code=\def\aeusepath{discard},
gray text/.code=\def\aenodesetupcol{\color{gray}}}
\let\arrowpath\relax
\let\aenodesetupcol\relax
\def\Arrowpath{%
\edef\linewidth{\the\pgflinewidth}
\pgfsetlinewidth{+2pt}
\pgfsetstrokecolor{black}
\pgfpathmoveto{\pgfpointanchor{chain-\cnt}{north}}
\pgfpathlineto{\pgfpointadd{\pgfpointanchor{chain-\cnt}{north}}{\pgfqpoint{0pt}{15pt}}}
\pgfsetarrowsstart{Stealth[length=+2ex,width=+1.5ex]}
\pgfusepath{stroke}
\pgfsetlinewidth{\linewidth}}
\pgfset{
x style/.style={inner xsep=+0.5pt, inner ysep=+1pt, none path},
f style/.style={x style, gray text},
o style/.style={x style, draw path},
m style/.style={o style, /utils/exec=\let\arrowpath\Arrowpath}}
\newcommand*\aenodesetup[1]{%
\vrule height1emdepth0ptwidth0pt\relax
\makebox[1em][c]{\aenodesetupcol#1}}
\newcommand*\aeaux[2]{%
\begingroup
\pgfset{#1 style}
\ifnum\cnt=1
\pgfnode{rectangle}{base}{\aenodesetup{#2}}{chain-\cnt}{\pgfusepath{\aeusepath}}
\else
\begingroup
\pgftransformshift{
\pgfpointadd{\pgfpointanchor{chain-\the\numexpr\cnt-1\relax}{base east}}
{\pgfqpoint{2pt}{0pt}}}
\pgfnode{rectangle}{base west}{\aenodesetup{#2}}{chain-\cnt}
{\pgfusepath{\aeusepath}}
\endgroup
\fi
\pgfsetstrokecolor{black}
\pgfpathmoveto{\pgfpointanchor{chain-\cnt}{south}}
\pgfpathlineto{\pgfpointadd{\pgfpointanchor{chain-\cnt}{south}}{\pgfqpoint{0pt}{-6pt}}}
\pgfusepath{stroke}
\arrowpath
\endgroup}
\newcommand*\aemarkup[3][]{
\pgfpicture[#1]
\foreach \aeI[count=\cnt,remember=\cnt] in {#2}% \cnt will be global after this
{\expandafter\aeaux\aeI}
\ifnum\cnt=#3\else
\edef\Cnt{\the\numexpr\cnt+1\relax}
\foreach \cnt in {\Cnt,...,#3}{\aeaux{x}{}}
\fi
\endpgfpicture\par}
\begin{document}
Diagram:
\aemarkup{mx , ox , ox , ox , ox , ox , ox , oM , ox , ox , ox , oa , ox , ob , oc}{15}
\aemarkup{fx , ox , mx , ox , ox , ox , ox , oM , ox , ox , ox , oa , ox , ob , oc}{15}
\aemarkup{fx , ox , fx , ox , mx , ox , ox , oM , ox , ox , ox , oa , ox , ob , oc}{15}
\aemarkup{fx , ox , fx , ox , fx , ox , mx , oM , ox , ox , ox , oa , ox , ob , oc}{15}
\aemarkup{fx , ox , fx , ox , fx , ox , fx , oM , mx , ox , ox , oa , ox , ob , oc}{15}
\aemarkup{fx , ox , fx , ox , fx , ox , fx , oM , fx , ox , mx , oa , ox , ob , oc}{15}
\aemarkup{fx , ox , fx , ox , fx , ox , fx , oM , fx , ox , fx , oa , mx , ob , oc}{15}
\aemarkup{fx , ox , fx , ox , fx , ox , fx , oM , fx , ox , fx , oa , fx , ob , mc}{15}
\aemarkup{mx , ox , ox , oM , ox , oa , ob , xc}{15}
\aemarkup{fx , ox , mx , oM , ox , oa , ob , xc}{15}
\aemarkup{fx , ox , fx , oM , mx , oa , ob , xc}{15}
\aemarkup{fx , ox , fx , oM , fx , oa , mb , xc}{15}
\aemarkup{mx , oM , oa , xb , xc}{15}
\aemarkup{fx , oM , ma , xb , xc}{15}
\aemarkup{mM, xa, xb, xc}{15}
\end{document}
+notation for PGFmath input so that it doesn’t parse the argument but simply uses TeX assignments (faster, see manual and my answer). Instead oftext width=1.5exusetext width=+1.5ex. Also, instead of(0,2.5ex)use(+0pt,+2.5ex): the+and no additional calculation of a vector (it does(0,0)+(0pt, 2.5ex)otherwise). — And, if you use that repeating code so often, you might just be coding it (or at least parts of it) in PGF, less parsing, more macro-ing. The loops are pretty simple, maybe replace them with a basic one. – Qrrbrbirlbel Oct 30 '13 at 00:34\usetikzlibrary{arrows.meta}? Tex complains about it when I try to typeset your code... – Alenanno Oct 30 '13 at 00:39tikzpictureenvironment for each instance of\aemarkup, have a single one and have\aemarkupjust input some TikZ code. You can use ascopeto adjust the vertical spacing, and a custom counter to move each level down the appropriate distance. That also means the rows of vertical bars can’t shift slightly (as in the penultimate and final lines). – alexwlchan Oct 30 '13 at 00:41externallibrary. – Qrrbrbirlbel Oct 30 '13 at 00:42text width=\widthof{M}, text depth=\depthof{j}, text height=\heightof{M}(or simply the widest, deepest and highest character you’re going to use) to the nodes, the should have the same disregarding their content. – Qrrbrbirlbel Oct 30 '13 at 01:18append after commandto the node, he nodes themselves might just be placed on achain. This all would make it possible to storem,x,oandfentirely in styles. (Simpler code, probably not really faster.) – Qrrbrbirlbel Oct 30 '13 at 01:20\path …;into PGF code and thus even more (there is for example no direct equivalent to nodes along paths (thinkpos,sloped, etc.). There are some relatedbut no direct “What is PGF? What is TikZ?” questions: Is there an advantage in using the pgf Basic Layer over tikz? and Conversion of pgf code into TikZ code? – Qrrbrbirlbel Oct 30 '13 at 01:30