#!/usr/bin/php doc/luadoc.json.gz # ## generate doc/ardourapi.json.gz (ardour header doxygen doc) # cd ../../tools/doxy2json # ./ardourdoc.sh # cd - # ## format HTML (using this scripterl) # php tools/fmt-luadoc.php > /tmp/luadoc.html # $options = getopt("m"); if (isset ($options['m'])) { $HTMLOUTPUT = false; ## set to false to output ardour-manual } else { $HTMLOUTPUT = true; ## set to false to output ardour-manual } ################################################################################ ################################################################################ $json = gzdecode (file_get_contents (dirname (__FILE__).'/../doc/luadoc.json.gz')); $doc = array (); $ardourversion = ''; foreach (json_decode ($json, true) as $b) { if (!isset ($b['type'])) { if (isset ($b['version'])) { $ardourversion = $b['version']; } continue; } # reserved lua words $b ['lua'] = preg_replace ('/:_end/', ':end', $b ['lua']); $b ['lua'] = preg_replace ('/:_type/', ':type', $b ['lua']); $b ['ldec'] = preg_replace ('/ const/', '', preg_replace ('/ const&/', '', $b['decl'])); if (isset ($b['ret'])) { $b['ret'] = preg_replace ('/ const/', '', preg_replace ('/ const&/', '', $b['ret'])); } $doc[] = $b; } if (count ($doc) == 0) { fwrite (STDERR, "Failed to read luadoc.json\n"); exit (1); } ################################################################################ ## Global result variables ################################################################################ $classlist = array (); $constlist = array (); ################################################################################ ## Pre-process the data, collect functions, parse arguments, cross reference ################################################################################ ################################################################################ # some internal helper functions first $funclist = array (); $classes = array (); $consts = array (); function my_die ($msg) { fwrite (STDERR, $msg."\n"); exit (1); } ##function ptr_strip ($ctype) { # # boost::shared_ptr> > > # # -> std::list # $ctype = preg_replace ('/boost::shared_ptr<([^>]*)[ ]*>/', '$1', $ctype); # return preg_replace ('/boost::shared_ptr<([^>]*)[ ]*>/', '$1', $ctype); #} function arg2lua ($argtype, $flags = 0) { global $classes; global $consts; # LuaBridge abstracts C++ references $flags |= preg_match ('/&$/', $argtype); $arg = preg_replace ('/&$/', '', $argtype); $arg = preg_replace ('/ $/', '', $arg); # filter out basic types $builtin = array ('float', 'double', 'bool', 'std::string', 'int', 'long', 'unsigned long', 'unsigned int', 'unsigned char', 'char', 'void', 'char*', 'unsigned char*', 'void*'); if (in_array ($arg, $builtin)) { return array ($arg => $flags); } # check Class declarations first foreach (array_merge ($classes, $consts) as $b) { if ($b['ldec'] == $arg) { return array ($b['lua'] => $flags); } } # strip class pointers -- TODO Check C'tor for given class $arg = preg_replace ('/[&*]*$/', '', $argtype); foreach (array_merge ($classes, $consts) as $b) { if ($b['ldec'] == $arg) { return array ($b['lua'] => $flags); } } if ($flags & 2) { return array ($argtype => ($flags | 4)); } else { return array ('--MISSING (' . $argtype . ')--' => ($flags | 4)); } } function stripclass ($classname, $name) { $classname .= ':'; if (strpos ($name, $classname) !== 0) { my_die ('invalid class prefix: ' .$classname. ' -- '. $name); } return substr ($name, strlen ($classname)); } function datatype ($decl) { # TODO handle spaces in type. Works because # we don't yet have templated types (with_space ) return substr ($decl, 0, strrpos ($decl, ' ')); } function luafn2class ($lua) { return substr ($lua, 0, strrpos ($lua, ':')); } function luafn2name ($lua) { $fn = strrpos ($lua, ':'); if ($fn !== 0 && strlen($lua) > $fn + 1) { return substr ($lua, $fn + 1); } my_die ('invalid class prefix: '. $name); } function checkclass ($b) { global $classlist; if (!isset ($classlist[luafn2class ($b['lua'])])) { my_die ('MISSING CLASS FOR '. print_r ($b['lua'], true)); } } # parse functions argument list to lua-names function decl2args ($decl) { $start = strrpos ($decl, '('); $end = strrpos ($decl, ')'); $args = substr ($decl, $start + 1, $end - $start - 1); $arglist = preg_split ('/, */', $args); $rv = array (); foreach ($arglist as $a) { if (empty ($a)) { continue; } $rv[] = arg2lua ($a); } return $rv; } function canonical_ctor ($b) { $rv = ''; if (preg_match('/[^(]*\(([^)*]*)\*\)(\(.*\))/', $b['decl'], $matches)) { $lc = luafn2class ($b['lua']); $cn = str_replace (':', '::', $lc); $fn = substr ($lc, 1 + strrpos ($lc, ':')); $rv = $cn . '::'. $fn . $matches[2]; } return $rv; } function canonical_decl ($b) { $rv = ''; $pfx = ''; # match clang's declatation format if (preg_match('/[^(]*\(([^)*]*)\*\)\((.*)\)/', $b['decl'], $matches)) { if (strpos ($b['type'], 'Free Function') !== false) { $pfx = str_replace (':', '::', luafn2class ($b['lua'])) . '::'; } $fn = substr ($b['lua'], 1 + strrpos ($b['lua'], ':')); $rv = $matches[1] . $fn . '('; $arglist = preg_split ('/, */', $matches[2]); $first = true; foreach ($arglist as $a) { if (!$first) { $rv .= ', '; }; $first = false; if (empty ($a)) { continue; } $a = preg_replace ('/([^>]) >/', '$1>', $a); $a = preg_replace ('/^Cairo::/', '', $a); // special case cairo enums $a = preg_replace ('/([^ ])&/', '$1 &', $a); $a = str_replace ('vector', 'std::vector', $a); $a = str_replace ('std::string', 'string', $a); $a = str_replace ('string const', 'const string', $a); $a = str_replace ('string', 'std::string', $a); $rv .= $a; } $rv .= ')'; } return $pfx . $rv; } ################################################################################ # step 1: build class indices foreach ($doc as $b) { if (strpos ($b['type'], "[C] ") === 0) { $classes[] = $b; $classlist[$b['lua']] = $b; if (strpos ($b['type'], 'Pointer Class') === false) { $classdecl[$b['ldec']] = $b; } } } foreach ($classes as $c) { if (strpos ($c['type'], 'Pointer Class') !== false) { continue; } if (isset ($c['parent'])) { if (isset ($classdecl[$c['parent']])) { $classlist[$c['lua']]['luaparent'][] = $classdecl[$c['parent']]['lua']; } else { my_die ('unknown parent class: ' . print_r ($c, true)); } } } # step 2: extract constants/enum foreach ($doc as $b) { switch ($b['type']) { case "Constant/Enum": case "Constant/Enum Member": if (strpos ($b['ldec'], '::') === false) { # for extern c enums, use the Lua Namespace $b['ldec'] = str_replace (':', '::', luafn2class ($b['lua'])); } $ns = str_replace ('::', ':', $b['ldec']); $constlist[$ns][] = $b; # arg2lua lookup $b['lua'] = $ns; $consts[] = $b; break; default: break; } } # step 3: process functions foreach ($doc as $b) { switch ($b['type']) { case "Constructor": case "Weak/Shared Pointer Constructor": checkclass ($b); $classlist[luafn2class ($b['lua'])]['ctor'][] = array ( 'name' => luafn2class ($b['lua']), 'args' => decl2args ($b['ldec']), 'cand' => canonical_ctor ($b) ); break; case "Data Member": checkclass ($b); $classlist[luafn2class ($b['lua'])]['data'][] = array ( 'name' => $b['lua'], 'ret' => arg2lua (datatype ($b['ldec'])) ); break; case "Static C Function": checkclass ($b); if (strpos ($b['lua'], 'ARDOUR:DataType:') === 0) { # special case ARDOUR:DataType convenience c'tor $args = array (); $ret = array (luafn2class ($b['lua']) => 0); $canon = 'ARDOUR::LuaAPI::datatype_ctor_'.strtolower (luafn2name ($b['lua'])).'(lua_State*)'; } else { my_die ('unhandled Static C: ' . print_r($b, true)); } $classlist[luafn2class ($b['lua'])]['func'][] = array ( 'bind' => $b, 'name' => $b['lua'], 'args' => $args, 'ret' => $ret, 'ref' => false, 'ext' => false, 'cand' => $canon ); break; case "C Function": # we required C functions to be in a class namespace case "Ext C Function": checkclass ($b); $args = array (array ('--lua--' => 0)); $ret = array ('...' => 0); $ns = luafn2class ($b['lua']); $cls = $classlist[$ns]; if (preg_match ('/.*<([^>]*)[ ]*>/', $cls['ldec'], $templ)) { # std::vector, std::list types switch (stripclass($ns, $b['lua'])) { case 'add': #$args = array (array ('LuaTable {'.$templ[1].'}' => 0)); $args = array (arg2lua ($templ[1], 2)); $ret = array ('LuaTable' => 0); break; case 'iter': $args = array (); $ret = array ('LuaIter' => 0); break; case 'table': $args = array (); $ret = array ('LuaTable' => 0); break; default: break; } } else if (strpos ($cls['type'], ' Array') !== false) { # catches C:FloatArray, C:IntArray $templ = preg_replace ('/[&*]*$/', '', $cls['ldec']); switch (stripclass($ns, $b['lua'])) { case 'array': $args = array (); $ret = array ('LuaMetaTable' => 0); break; case 'get_table': $args = array (); $ret = array ('LuaTable' => 0); break; case 'set_table': $args = array (array ('LuaTable {'.$templ.'}' => 0)); $ret = array ('void' => 0); break; default: break; } } $classlist[luafn2class ($b['lua'])]['func'][] = array ( 'bind' => $b, 'name' => $b['lua'], 'args' => $args, 'ret' => $ret, 'ref' => true, 'ext' => true, 'cand' => canonical_decl ($b) ); break; case "Free C Function": $funclist[luafn2class ($b['lua'])][] = array ( 'bind' => $b, 'name' => $b['lua'], 'args' => $args, 'ret' => $ret, 'ref' => false, 'ext' => true, 'cand' => str_replace (':', '::', $b['lua']).'(lua_State*)' ); break; case "Free Function": case "Free Function RefReturn": $funclist[luafn2class ($b['lua'])][] = array ( 'bind' => $b, 'name' => $b['lua'], 'args' => decl2args ($b['ldec']), 'ret' => arg2lua ($b['ret']), 'ref' => (strpos ($b['type'], "RefReturn") !== false), 'cand' => canonical_decl ($b) ); break; case "Member Function": case "Member Function RefReturn": case "Member Pointer Function": case "Weak/Shared Pointer Function": case "Weak/Shared Pointer Function RefReturn": case "Weak/Shared Null Check": case "Static Member Function": checkclass ($b); $classlist[luafn2class ($b['lua'])]['func'][] = array ( 'bind' => $b, 'name' => $b['lua'], 'args' => decl2args ($b['ldec']), 'ret' => arg2lua ($b['ret']), 'ref' => (strpos ($b['type'], "RefReturn") !== false), 'cand' => canonical_decl ($b) ); break; case "Weak/Shared Pointer Cast": checkclass ($b); $classlist[luafn2class ($b['lua'])]['cast'][] = array ( 'bind' => $b, 'name' => $b['lua'], 'args' => decl2args ($b['ldec']), 'ret' => arg2lua ($b['ret']), 'ref' => (strpos ($b['type'], "RefReturn") !== false), 'cand' => canonical_decl ($b) ); break; case "Constant/Enum": case "Constant/Enum Member": # already handled -> $consts break; default: if (strpos ($b['type'], "[C] ") !== 0) { my_die ('unhandled type: ' . $b['type']); } break; } } # step 4: collect/group/sort # step 4a: unify weak/shared Ptr classes foreach ($classlist as $ns => $cl) { if (strpos ($cl['type'], ' Array') !== false) { $classlist[$ns]['arr'] = true; continue; } foreach ($classes as $c) { if ($c['lua'] == $ns) { if (strpos ($c['type'], 'Pointer Class') !== false) { $classlist[$ns]['ptr'] = true; $classlist[$ns]['decl'] = 'boost::shared_ptr< '.$c['decl']. ' >, boost::weak_ptr< '.$c['decl']. ' >'; break; } } } } # step4b: sanity check foreach ($classlist as $ns => $cl) { if (isset ($classes[$ns]['parent']) && !isset ($classlist[$ns]['luaparent'])) { my_die ('missing parent class: ' . print_r ($cl, true)); } } # step 4c: merge free functions into classlist foreach ($funclist as $ns => $fl) { if (isset ($classlist[$ns])) { my_die ('Free Funcion in existing namespace: '.$ns.' '. print_r ($ns, true)); } $classlist[$ns]['func'] = $fl; $classlist[$ns]['free'] = true; } # step 4d: order to chaos # no array_multisort() here, sub-types are sorted after merging parents ksort ($classlist); ################################################################################ ################################################################################ ################################################################################ #### -- split here -- #### # from here on, only $classlist and $constlist arrays are relevant. # we also pull in C++ header annotation from doxygen to $api # read documentation from doxygen $json = gzdecode (file_get_contents (dirname (__FILE__).'/../doc/ardourapi.json.gz')); $api = array (); foreach (json_decode ($json, true) as $a) { if (!isset ($a['decl'])) { continue; } if (empty ($a['decl'])) { continue; } $canon = str_replace (' *', '*', $a['decl']); $api[$canon] = $a; } # keep track of found/missing doc $dox_found = 0; $dox_miss = 0; # retrive a value from $api function doxydoc ($canonical_declaration) { global $api; global $dox_found; global $dox_miss; if (isset ($api[$canonical_declaration])) { $dox_found++; return $api[$canonical_declaration]['doc']; } else { $dox_miss++; return ''; } } ################################################################################ # OUTPUT ################################################################################ ################################################################################ # Helper functions define ('NL', "\n"); # constructors, enums (constants) use a dot. (e.g. "LuaOSC.Address" -> "LuaOSC.Address" ) function ctorname ($name) { return htmlentities (str_replace (':', '.', $name)); } # strip class prefix (e.g "Evoral:MidiEvent:channel" -> "channel") function shortname ($name) { return htmlentities (substr ($name, strrpos ($name, ':') + 1)); } # retrieve variable name from array["VARNAME"] => FLAGS function varname ($a) { return array_keys ($a)[0]; } # recusively collect class parents (derived classes) function traverse_parent ($ns, &$inherited) { global $classlist; $rv = ''; if (isset ($classlist[$ns]['luaparent'])) { $parents = array_unique ($classlist[$ns]['luaparent']); asort ($parents); foreach ($parents as $p) { if (!empty ($rv)) { $rv .= ', '; } $rv .= typelink ($p); $inherited[$p] = $classlist[$p]; traverse_parent ($p, $inherited); } } return $rv; } # create a cross-reference to a type (class or enum) # *all* links are generated here, currently anchors on a single page. function typelink ($a, $short = false, $argcls = '', $linkcls = '', $suffix = '') { global $classlist; global $constlist; if (isset($classlist[$a]['free'])) { return ''.($short ? shortname($a) : ctorname($a)).$suffix.''; } else if (in_array ($a, array_keys ($classlist))) { return ''.($short ? shortname($a) : htmlentities($a)).$suffix.''; } else if (in_array ($a, array_keys ($constlist))) { return ''.($short ? shortname($a) : ctorname($a)).$suffix.''; } else { return ''.htmlentities($a).$suffix.''; } } # output format function arguments function format_args ($args) { $rv = ' ('; $first = true; foreach ($args as $a) { if (!$first) { $rv .= ', '; }; $first = false; $flags = $a[varname ($a)]; if ($flags & 2) { $rv .= 'LuaTable {'.typelink (varname ($a), true, 'em').'}'; } elseif ($flags & 1) { $rv .= typelink (varname ($a), true, 'em', '', '&'); } else { $rv .= typelink (varname ($a), true, 'em'); } } $rv .= ')'; return $rv; } # format doxygen documentation for class-definition function format_doxyclass ($cl) { $rv = ''; if (isset ($cl['decl'])) { $doc = doxydoc ($cl['decl']); if (!empty ($doc)) { $rv.= '
'.$doc.'
'.NL; } } return $rv; } # format doxygen documentation for class-members function format_doxydoc ($f) { $rv = ''; if (isset ($f['cand'])) { $doc = doxydoc ($f['cand']); if (!empty ($doc)) { $rv.= '
'.$doc; $rv.= '
'.NL; } else if (0) { # debug $rv.= '

'.htmlentities($f['cand']).'

'; $rv.= ''.NL; } } return $rv; } # usort() callback for class-members function name_sort_cb ($a, $b) { return strcmp ($a['name'], $b['name']); } # main output function for every class function format_class_members ($ns, $cl, &$dups) { $rv = ''; # print contructor - if any if (isset ($cl['ctor'])) { usort ($cl['ctor'], 'name_sort_cb'); $rv.= ' Constructor'.NL; foreach ($cl['ctor'] as $f) { $rv.= ' ℂ'; $rv.= ''.ctorname ($f['name']).''; $rv.= format_args ($f['args']); $rv.= ''.NL; # doxygen documentation (may be empty) $rv.= format_doxydoc($f); } } # strip duplicates (inherited or derived methods) # e.g AudioTrack -> Track -> Route -> SessionObject -> Stateful # all 5 have "isnil()" $nondups = array (); if (isset ($cl['func'])) { foreach ($cl['func'] as $f) { if (in_array (stripclass ($ns, $f['name']), $dups)) { continue; } $nondups[] = $f; } } # print methods - if any if (count ($nondups) > 0) { usort ($nondups, 'name_sort_cb'); $rv.= ' Methods'.NL; foreach ($nondups as $f) { $dups[] = stripclass ($ns, $f['name']); # return value/type $rv.= ' '; if ($f['ref'] && isset ($f['ext'])) { # external C functions $rv.= ''.varname ($f['ret']).''; } elseif ($f['ref'] && varname ($f['ret']) == 'void') { # void functions with reference args $rv.= 'LuaTable(...)'; } elseif ($f['ref']) { # functions with reference args and return value $rv.= 'LuaTable('.typelink (varname ($f['ret']), true, 'em').', ...)'; } else { # normal class members $rv.= typelink (varname ($f['ret']), true, 'em'); } # function declaration and arguments $rv.= ''; $rv.= ''.stripclass ($ns, $f['name']).''; $rv.= format_args ($f['args']); $rv.= ''.NL; # doxygen documentation (may be empty) $rv.= format_doxydoc($f); } } # print cast - if any if (isset ($cl['cast'])) { usort ($cl['cast'], 'name_sort_cb'); $rv.= ' Cast'.NL; foreach ($cl['cast'] as $f) { $rv.= ' '; $rv.= typelink (varname ($f['ret']), true, 'em'); # function declaration and arguments $rv.= ''; $rv.= ''.stripclass ($ns, $f['name']).''; $rv.= format_args ($f['args']); $rv.= ''.NL; # doxygen documentation (may be empty) $rv.= format_doxydoc($f); } } # print data members - if any if (isset ($cl['data'])) { usort ($cl['data'], 'name_sort_cb'); $rv.= ' Data Members'.NL; foreach ($cl['data'] as $f) { $rv.= ' '.typelink (array_keys ($f['ret'])[0], false, 'em').''; $rv.= ''.stripclass ($ns, $f['name']).''; $rv.= ''.NL; } } return $rv; } ################################################################################ # Start Output if ($HTMLOUTPUT) { ?> Ardour Lua Bindings

Ardour Lua Bindings

Class Documentation  |  Enum/Constants  |  Index

--- layout: default style: luadoc title: Class Reference ---

This documention is far from complete may be inaccurate and subject to change.

Overview

The top-level entry point are and . Most other Classes are used indirectly starting with a Session function. e.g. Session:get_routes().

A few classes are dedicated to certain script types, e.g. Lua DSP processors have exclusive access to and . Action Hooks Scripts to etc.

Detailed documentation (parameter names, method description) is not yet available. Please stay tuned.

Short introduction to Ardour classes

Ardour's structure is object oriented. The main object is the Session. A Session contains Audio Tracks, Midi Tracks and Busses. Audio and Midi tracks are derived from a more general "Track" Object, which in turn is derived from a "Route" (aka Bus). (We say "An Audio Track is-a Track is-a Route"). Tracks contain specifics. For Example a track has-a diskstream (for file i/o).

Operations are performed on objects. One gets a reference to an object and then calls a method. e.g obj = Session:route_by_name("Audio") obj:set_name("Guitar").

Lua automatically follows C++ class inheritance. e.g one can directly call all SessionObject and Route methods on Track object. However lua does not automatically promote objects. A Route object which just happens to be a Track needs to be explicily cast to a Track. Methods for casts are provided with each class. Note that the cast may fail and return a nil reference.

Likewise multiple inheritance is a non-trivial issue in lua. To avoid performance penalties involved with lookups, explicit casts are required in this case. One example is which is-a StatefulDestructible which inhertis from both Stateful and Destructible.

Object lifetimes are managed by the Session. Most Objects cannot be directly created, but one asks the Session to create or destroy them. This is mainly due to realtime constrains: you cannot simply remove a track that is currently processing audio. There are various factory methods for object creation or removal.

Pass by Reference

Since lua functions are closures, C++ methods that pass arguments by reference cannot be used as-is. All parameters passed to a C++ method which uses references are returned as Lua Table. If the C++ method also returns a value it is prefixed. Two parameters are returned: the value and a Lua Table holding the parameters.

C++
void set_ref (int& var, long& val)
{
	printf ("%d %ld\n", var, val);
	var = 5;
	val = 7;
}
Lua
local var = 0;
ref = set_ref (var, 2);
-- output from C++ printf()
0 2
-- var is still 0 here
print (ref[1], ref[2])
5 7
int set_ref2 (int &var, std::string unused)
{
	var = 5;
	return 3;
}
rv, ref = set_ref2 (0, "hello");
print (rv, ref[1], ref[2])
3 5 hello

Pointer Classes

Libardour makes extensive use of reference counted boost::shared_ptr to manage lifetimes. The Lua bindings provide a complete abstration of this. There are no pointers in lua. For example a is a pointer in C++, but lua functions operate on it like it was a class instance.

shared_ptr are reference counted. Once assigned to a lua variable, the C++ object will be kept and remains valid. It is good practice to assign references to lua local variables or reset the variable to nil to drop the ref.

All pointer classes have a isnil () method. This is for two cases: Construction may fail. e.g. .newplugin() may not be able to find the given plugin and hence cannot create an object.

The second case if for boost::weak_ptr. As opposed to boost::shared_ptr weak-pointers are not reference counted. The object may vanish at any time. If lua code calls a method on a nil object, the interpreter will raise an exception and the script will not continue. This is not unlike a = nil a:test() which results in en error "attempt to index a nil value".

From the lua side of things there is no distinction between weak and shared pointers. They behave identically. Below they're inidicated in orange and have an arrow to indicate the pointer type. Pointer Classes cannot be created in lua scripts. It always requires a call to C++ to create the Object and obtain a reference to it.

Class Documentation'.NL; foreach ($classlist as $ns => $cl) { $dups = array (); $tbl = format_class_members ($ns, $cl, $dups); # format class title - depending on type if (empty ($tbl)) { # classes with no members (no ctor, no methods, no data) echo '

 '.htmlentities ($ns).'

'.NL; } else if (isset ($classlist[$ns]['free'])) { # free functions (no class) echo '

 '.ctorname($ns).'

'.NL; } else if (isset ($classlist[$ns]['arr'])) { # C Arrays echo '

 '.htmlentities ($ns).'

'.NL; } else if (isset ($classlist[$ns]['ptr'])) { # Pointer Classes echo '

 '. htmlentities ($ns).'

'.NL; } else { # Normal Class echo '

 '.htmlentities ($ns).'

'.NL; } # show original C++ declaration if (isset ($cl['decl'])) { echo '

C‡: '.htmlentities ($cl['decl']).'

'.NL; } # print class inheritance (direct parent *name* only) $inherited = array (); $isa = traverse_parent ($ns, $inherited); if (!empty ($isa)) { echo '

is-a: '.$isa.'

'.NL; } echo '
'.NL; # class documentation (if any) echo format_doxyclass ($cl); # member documentation if (empty ($tbl)) { echo '

This class object is only used indirectly as return-value and function-parameter. It provides no methods by itself.

'.NL; } else { echo ''.NL; echo $tbl; echo '
'.NL; } # traverse parent classes (all inherited members) foreach ($inherited as $pns => $pcl) { $tbl = format_class_members ($pns, $pcl, $dups); if (!empty ($tbl)) { echo '

Inherited from '.$pns.'

'.NL; echo ''.NL; echo $tbl; echo '
'.NL; } } } #################### # Enum and Constants echo '

Enum/Constants

'.NL; foreach ($constlist as $ns => $cs) { echo '

 '.ctorname ($ns).'

'.NL; echo '
    '.NL; foreach ($cs as $c) { echo '
  • '.ctorname ($c['lua']).'
  • '.NL; } echo '
'.NL; } ###################### # Index of all classes echo '

Class Index

'.NL; echo '
    '.NL; foreach ($classlist as $ns => $cl) { echo '
  • '.typelink($ns).'
  • '.NL; } echo '
'.NL; # see how far there is still to go... fwrite (STDERR, "Found $dox_found annotations. missing: $dox_miss\n"); echo ''.NL; ?>
Ardour  - 
'.NL; echo ''.NL; echo ''.NL; }