/[svn]/doc/docbase/instrument_scripts/nksp/01_nksp.html
ViewVC logotype

Diff of /doc/docbase/instrument_scripts/nksp/01_nksp.html

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 3119 by schoenebeck, Fri Apr 21 16:02:47 2017 UTC revision 3817 by schoenebeck, Sat Aug 29 09:16:38 2020 UTC
# Line 22  Line 22 
22        <img src="nksp_file.png" style="height:111px; margin-right:12px;">        <img src="nksp_file.png" style="height:111px; margin-right:12px;">
23        NKSP stands for "is <b>N</b>ot <b>KSP</b>", which denotes its distinction        NKSP stands for "is <b>N</b>ot <b>KSP</b>", which denotes its distinction
24        to an existing proprietary language called <i>KSP</i>.        to an existing proprietary language called <i>KSP</i>.
25        NSKP is a script language specifically designed to write real-time capable        NKSP is a script language specifically designed to write real-time capable
26        software extensions to LinuxSampler's sampler engines that can be bundled        software extensions to LinuxSampler's sampler engines that can be bundled
27        individually with sounds by sound designers themselves.        individually with sounds by sound designers themselves.
28    
# Line 67  on ??event-name?? Line 67  on ??event-name??
67  end on  end on
68      </code>      </code>
69      <p>      <p>
70        There are currently four events available:        There are currently six events available:
71      </p>      </p>
72        <table>        <table>
73          <tr>          <tr>
# Line 77  end on Line 77  end on
77            <td><code>on note</code></td> <td>This event handler is executed when a new note was triggered, i.e. when hitting a key on a MIDI keyboard.</td>            <td><code>on note</code></td> <td>This event handler is executed when a new note was triggered, i.e. when hitting a key on a MIDI keyboard.</td>
78          </tr>          </tr>
79          <tr>          <tr>
80            <td><code>on release</code></td> <td>This event handler is executed when a new note was released, i.e. when releasing a key on a MIDI keyboard.</td>            <td><code>on release</code></td> <td>This event handler is executed when a note was released, i.e. when releasing a key on a MIDI keyboard.</td>
81          </tr>          </tr>
82          <tr>          <tr>
83            <td><code>on controller</code></td> <td>This event handler is executed when a MIDI control change event occurred. For instance when turning the modulation wheel at a MIDI keyboard.</td>            <td><code>on controller</code></td> <td>This event handler is executed when a MIDI control change event occurred. For instance when turning the modulation wheel at a MIDI keyboard.</td>
84          </tr>          </tr>
85          <tr>          <tr>
86              <td><code>on rpn</code></td> <td>This event handler is executed when a MIDI <i>RPN</i> event occurred.</td>
87            </tr>
88            <tr>
89              <td><code>on nrpn</code></td> <td>This event handler is executed when a MIDI <i>NRPN</i> event occurred.</td>
90            </tr>
91            <tr>
92            <td><code>on init</code></td> <td>Executed only once, as very first event handler, right after the script had been loaded. This code block is usually used to initialize variables in your script with some initial, useful data.</td>            <td><code>on init</code></td> <td>Executed only once, as very first event handler, right after the script had been loaded. This code block is usually used to initialize variables in your script with some initial, useful data.</td>
93          </tr>          </tr>
94        </table>        </table>
# Line 147  end on Line 153  end on
153        Please note that you can hardly find MIDI keyboards which support release        Please note that you can hardly find MIDI keyboards which support release
154        velocity. So with most keyboards this value will be 127.        velocity. So with most keyboards this value will be 127.
155      </p>      </p>
156        
157      <h3>Controller Events</h3>      <h3>Controller Events</h3>
158      <p>        <p>  
159        Now let's extend the first script to not only show note-on and note-off        Now let's extend the first script to not only show note-on and note-off
# Line 180  end on Line 186  end on
186      </p>      </p>
187      <p>      <p>
188        There is some special aspect you need to be aware about: in contrast to the MIDI standard,        There is some special aspect you need to be aware about: in contrast to the MIDI standard,
189        monophonic aftertouch (a.k.a. channel pressure) and pitch beend wheel are        monophonic aftertouch (a.k.a. channel pressure) and pitch bend wheel are
190        handled by NKSP as if they were regular MIDI controllers. So a value change        handled by NKSP as if they were regular MIDI controllers. So a value change
191        of one of those two triggers a regular <code>controller</code> event handler        of one of those two triggers a regular <code>controller</code> event handler
192        to be executed. To obtain the current aftertouch value you can use        to be executed. To obtain the current aftertouch value you can use
193        <code>%CC[$VCC_MONO_AT]</code>, and to get the current pitch bend wheel        <code>%CC[$VCC_MONO_AT]</code>, and to get the current pitch bend wheel
194        value use <code>%CC[$VCC_PITCH_BEND]</code>.        value use <code>%CC[$VCC_PITCH_BEND]</code>.
195      </p>      </p>
196        
197        <h3>RPN / NRPN Events</h3>
198        <p>
199          There are also dedicated event handlers for
200          MIDI <i title="Registered Parameter Number">RPN</i> and
201          <i title="Non-Registered Parameter Number">NRPN</i>
202          events:
203        </p>
204        <code>
205    on rpn
206      message("RPN address msb=" & msb($RPN_ADDRESS) & ",lsb=" & lsb($RPN_ADDRESS) &
207              "-> value msb=" & msb($RPN_VALUE) & ",lsb="  & lsb($RPN_VALUE))
208      if ($RPN_ADDRESS = 2)
209        message("Standard Coarse Tuning RPN received")
210      end if
211    end on
212    
213    on nrpn
214      message("NRPN address msb=" & msb($RPN_ADDRESS) & ",lsb=" & lsb($RPN_ADDRESS) &
215              "-> value msb=" & msb($RPN_VALUE) & ",lsb="  & lsb($RPN_VALUE))
216    end on
217        </code>
218        <p>
219          Since MIDI RPN and NRPN events are actually MIDI controller events,
220          you might as well handle these with the previous
221          <code>controller</code> event handler. But since RPN and NRPN messages
222          are not just one MIDI message, but rather always handled by a set of
223          individual MIDI messages, and since the
224          precise set and sequence of actual MIDI commands sent varies between
225          vendors and even among individual of their products, it highly makes sense to
226          use these two specialized event handlers for these instead, because the
227          sampler will already relief you from that burden to deal with all those
228          low-level MIDI event processing issues and all their wrinkles involved
229          when handling RPNs and NRPNs.
230        </p>
231        <note>
232          Even though there are two separate, dedicated event handlers for RPN and NRPN events,
233          they both share the same built-in variable names as you can see in the
234          example above.
235        </note>
236        <p>
237          So by reading <code>$RPN_ADDRESS</code> you get the RPN / NRPN parameter
238          number that had been changed, and <code>$RPN_VALUE</code> represents the
239          new value of that RPN / NRPN parameter. Note that these two built-in
240          variables are a 14-bit representation of the parameter number and new
241          value. So their possible value range is <code>0 .. 16383</code>. If you
242          rather want to use their (in MIDI world) more common separated two 7 bit
243          values instead, then you can easily do that by wrapping them into either
244          <code>msb()</code> or <code>lsb()</code> calls like also demonstrated above.
245        </p>
246    
247      <h3>Script Load Event</h3>      <h3>Script Load Event</h3>
248      <p>      <p>
249        As the last one of the four event types available with NKSP, the following        As the last one of the six event types available with NKSP, the following
250        is an example of an <code>init</code> event handler.        is an example of an <code>init</code> event handler.
251      </p>      </p>
252      <code>      <code>
# Line 214  end on Line 270  end on
270        had in mind when you wrote a certain script three years ago, and also if        had in mind when you wrote a certain script three years ago, and also if
271        some other developer might need to continue working on your scripts one        some other developer might need to continue working on your scripts one
272        day, you should place as many comments into your scripts as possible. A        day, you should place as many comments into your scripts as possible. A
273        comment in NKSP is everything that is nested into a an opening and closing        comment in NKSP is everything that is nested into an opening and closing
274        pair of curly braces.        pair of curly braces.
275      </p>      </p>
276      <code>{ This is a comment. }</code>      <code>{ This is a comment. }</code>
# Line 254  end on Line 310  end on
310      <p>      <p>
311        The left hand side's <code>??variable-name??</code> is an arbitrary name        The left hand side's <code>??variable-name??</code> is an arbitrary name
312        you can chose for your variable. That name might consist of English        you can chose for your variable. That name might consist of English
313        letters A to Z (lower and upper case) and the underscore character "<code>_</code>".        letters A to Z (lower and upper case), digits (<code>0</code> to <code>9</code>),
314          and the underscore character "<code>_</code>".
315        Variable names must be unique. So you can neither declare several variables        Variable names must be unique. So you can neither declare several variables
316        with the same name, nor can you use a name for your variable that is        with the same name, nor can you use a name for your variable that is
317        already been reserved by <i>built-in variables</i>.        already been reserved by <i>built-in variables</i>.
# Line 298  end on Line 355  end on
355    
356      <h3>Variable Types</h3>      <h3>Variable Types</h3>
357      <p>      <p>
358        There are currently three different variable types, which you can easily        There are currently five different variable types, which you can easily
359        recognize upon their first character.        recognize upon their first character.
360      </p>      </p>
361      <table>      <table>
# Line 312  end on Line 369  end on
369          <td><code>%??variable-name??</code></td> <td>Integer Array</td> <td>Stores a certain amount of integer number values.</td>          <td><code>%??variable-name??</code></td> <td>Integer Array</td> <td>Stores a certain amount of integer number values.</td>
370        </tr>        </tr>
371        <tr>        <tr>
372            <td><code>~??variable-name??</code></td> <td>Real Number Scalar</td> <td>Stores one single real (floating point) number value.</td>
373          </tr>
374          <tr>
375            <td><code>???variable-name??</code></td> <td>Real Number Array</td> <td>Stores a certain amount of real (floating point) number values.</td>
376          </tr>
377          <tr>
378          <td><code>@??variable-name??</code></td> <td>String</td> <td>Stores one text string.</td>          <td><code>@??variable-name??</code></td> <td>String</td> <td>Stores one text string.</td>
379        </tr>        </tr>
380      </table>      </table>
# Line 412  end on Line 475  end on
475      </p>      </p>
476      <p>      <p>
477        Let's assume you wanted to write an instrument script that shall resemble        Let's assume you wanted to write an instrument script that shall resemble
478        a simple delay effect. You could do that by writing an note event handler        a simple delay effect. You could do that by writing a note event handler
479        that automatically triggers several new notes for each note being        that automatically triggers several new notes for each note being
480        triggered on a MIDI keyboard. The following example demonstrates how that        triggered on a MIDI keyboard. The following example demonstrates how that
481        could be achieved.        could be achieved.
482      </p>      </p>
     <note>  
       You need at least LinuxSampler 2.0.0.svn2 or higher for the following  
       example to work as described and as expected. Refer to the notes of the  
       <code>wait()</code> function reference documentation for more  
       informations about this issue.  
     </note>  
483      <code>      <code>
484  on init  on init
485    { The amount of notes to play }    { The amount of notes to play }
# Line 477  end on Line 534  end on
534        happening when executing that script exactly: Each time you play a note        happening when executing that script exactly: Each time you play a note
535        on your keyboard, a new instance of the <code>note</code> event handler        on your keyboard, a new instance of the <code>note</code> event handler
536        will be spawned and executed by the sampler. In all our examples so far        will be spawned and executed by the sampler. In all our examples so far
537        our scripts were so simple, that in practice only one handler instance        our scripts were so simple, that in practice only one event handler instance
538        was executed at a time. This is different in this case though. Because        was executed at a time. This is different in this case though. Because
539        by calling the <code>wait()</code> function, the respective handler        by calling the <code>wait()</code> function, the respective handler
540        execution instance is paused for a while and in total each handler        execution instance is paused for a while and in total each handler
# Line 486  end on Line 543  end on
543        you play multiple, successive notes on your keyboard in short time, you        you play multiple, successive notes on your keyboard in short time, you
544        will have several instances of the <code>note</code> event handler running        will have several instances of the <code>note</code> event handler running
545        simultaniously. And that's where the problem starts. Because by default,        simultaniously. And that's where the problem starts. Because by default,
546        as said, all variables are global variables. So the handler instances        as said, all variables are global variables. So the event handler instances
547        which are now running in parallel, are all reading and modifying the same        which are now running in parallel, are all reading and modifying the same
548        data. Thus the individual handler instances will modify the        data. Thus the individual event handler instances will modify the
549        <code>$i</code> and <code>$velocity</code> variables of each other, causing        <code>$i</code> and <code>$velocity</code> variables of each other, causing
550        an undesired misbehavior.        an undesired misbehavior.
551      </p>      </p>
# Line 566  end on Line 623  end on
623      </p>      </p>
624      <p>      <p>
625        Please note that the <i>polyphonic</i> qualifier only exists for integer        Please note that the <i>polyphonic</i> qualifier only exists for integer
626        variables. So you cannot declare polyphonic string variables, nor can you        variables and real number variables (scalars).
627          So you cannot declare polyphonic string variables, nor can you
628        declare polyphonic array variables. Like in the previous explanation,        declare polyphonic array variables. Like in the previous explanation,
629        this is due to the fact that it would consume a huge amount of memory        this is due to the fact that it would consume a huge amount of memory
630        for such variables. And with string variables and array variables, the        for such variables. And with string variables and array variables, the
631        required amount of memory would be much higher than with simple integer        required amount of memory would be much higher than with simple integer or
632        variables.        real number variables.
633      </p>      </p>
634      <p>      <p>
635        As summary, the following are guideline rules describing when you should        As summary, the following are guideline rules describing when you should
# Line 980  end on Line 1038  end on
1038        execution of the script instance by calling <code>exit()</code>. The latter        execution of the script instance by calling <code>exit()</code>. The latter
1039        is important in this example, because otherwise the script execution instances would        is important in this example, because otherwise the script execution instances would
1040        continue to run in this endless loop forever, even after the respectives        continue to run in this endless loop forever, even after the respectives
1041        notes are gone. Which would let your CPU usage to increase with every new note        notes are gone. Which would let your CPU usage increase with every new note
1042        and would never decrease again.        and would never decrease again.
1043        This behavior of the sampler is not a bug, it is intended, since there may        This behavior of the sampler is not a bug, it is intended, since there may
1044        also be cases where you want to do certain things by script even after the        also be cases where you want to do certain things by script even after the
# Line 1018  end on Line 1076  end on
1076        substantially differs calling built-in functions from calling user functions.        substantially differs calling built-in functions from calling user functions.
1077      </p>      </p>
1078    
1079        <h3>Synchronized Blocks</h3>
1080        <p>
1081          When we introduced the <a href="#polyphonic_variables">polyphonic keyword</a>
1082          previously, we learned that a script may automatically be suspended by
1083          the sampler at any time and then your script is thus sleeping for an
1084          arbitrary while. The sampler must do such auto suspensions under certain
1085          situations in cases where an instrument script may become a hazard for the
1086          sampler's overall real-time stability. If the sampler would not do so, then
1087          instrument scripts might easily cause audio dropouts, or at worst, buggy
1088          instrument scripts might even lock up the entire sampler in an endless
1089          loop. So auto suspension is an essential feature of the sampler's real-time
1090          instrument script engine.
1091        </p>
1092        <p>
1093          Now the problem as a script author is that you don't really know beforehand
1094          why and when your script might get auto suspended by the sampler. And when
1095          you are working on more complex, sophisticated scripts, you will notice
1096          that this might indeed be a big problem in certain sections of your scripts.
1097          Because in practice, a sophisticated script often has at least one certain
1098          consecutive portion of statements which must be executed in strict consecutive order
1099          by the sampler, which might otherwise cause concurrency issues and thus
1100          misbehavior of your script if that sensible code section was auto suspended
1101          in between. A typical example of such concurrency sensible code sections are
1102          statements which are reading and conditionally modifying global variables.
1103          If your script gets auto suspended in such a code section, another
1104          script handler instance might then interfere and change those global
1105          variables in between.
1106        </p>
1107        <p>
1108          To avoid that, you can place such a sensible code section at the very beginning
1109          of your event handler. For example consider you might be writing a custom
1110          <i title="A consecutive pitch glide from one note to another note.">glissando</i>
1111          script starting like this:
1112        </p>
1113        <code>
1114    on init
1115      declare $keysDown
1116      declare $firstNoteID
1117      declare $firstNoteNr
1118      declare $firstVelocity
1119    end on
1120    
1121    on note
1122      { The concurrency sensible code section for the "first active" note. }
1123      inc($keysDown)
1124      if ($keysDown = 1 or event_status($firstNoteID) = $EVENT_STATUS_INACTIVE)
1125        $firstNoteID = $EVENT_ID
1126        $firstNoteNr = $EVENT_NOTE
1127        $firstVelocity = $EVENT_VELOCITY
1128        exit { return from event handler here }
1129      end if
1130    
1131      { The non-sensible code for all other subsequent notes would go here. }
1132    end on
1133    
1134    on release
1135      dec($keysDown)
1136    end on
1137        </code>
1138        <p>
1139          Because the earlier statements are executed in an event handler, the higher
1140          the chance that they will never get auto suspended. And with those couple of
1141          lines in the latter example you might even be lucky that it won't ever get
1142          suspended in that sensible code section at least. However when it comes to live
1143          concerts you don't really want to depend on luck, and in practice such a
1144          sensible code section might be bigger than this one.
1145        </p>
1146        <p>
1147          That's why we introduced <code>synchronized</code> code blocks for the
1148          NKSP language, which have the following form:
1149        </p>
1150        <code>
1151    synchronized
1152    
1153      ??statements??
1154    
1155    end synchronized
1156        </code>
1157        <p>
1158          All <code>??statements??</code> which you put into such a synchronized
1159          code block are guaranteed that they will never get auto suspended by
1160          the sampler.
1161        </p>
1162        <note>
1163          Such <code>synchronized</code> blocks are a language extension which
1164          is only available with NKSP. KSP does not support <code>synchronized</code> blocks.
1165        </note>
1166        <p>
1167          So to make our previous example concurrency safe, we would
1168          change it like this:
1169        </p>
1170        <code>
1171    on init
1172      declare $keysDown
1173      declare $firstNoteID
1174      declare $firstNoteNr
1175      declare $firstVelocity
1176    end on
1177    
1178    on note
1179      { The concurrency sensible code section for the "first active" note. }
1180      synchronized
1181        inc($keysDown)
1182        if ($keysDown = 1 or event_status($firstNoteID) = $EVENT_STATUS_INACTIVE)
1183          $firstNoteID = $EVENT_ID
1184          $firstNoteNr = $EVENT_NOTE
1185          $firstVelocity = $EVENT_VELOCITY
1186          exit { return from event handler here }
1187        end if
1188      end synchronized
1189    
1190      { The non-sensible code for all other subsequent notes would go here. }
1191    end on
1192    
1193    on release
1194      dec($keysDown)
1195    end on
1196        </code>
1197        <p>
1198          If you are already familiar with some programming languages, then you
1199          might already have seen such synchronized code block concepts
1200          in languages like e.g. Java. This technique really provides an easy way
1201          to protect certain sections of your script against concurrency issues.
1202        </p>
1203        <note class="important">
1204          You <b>must</b> use such <code>synchronized</code> code blocks only with great
1205          care! If the amount of statements being executed in your synchronized block
1206          is too large, then you will get audio dropouts. If you even use loops in
1207          synchronized code blocks, then the entire sampler might even become
1208          unresponsive in case your script is buggy!
1209        </note>
1210    
1211      <h2>Operators</h2>      <h2>Operators</h2>
1212      <p>      <p>
1213        A programming language provides so called <i>operators</i> to perform        A programming language provides so called <i>operators</i> to perform
# Line 1063  end on Line 1253  end on
1253        Keep in mind that with logical operators shown above,        Keep in mind that with logical operators shown above,
1254        all integer values other than <code>0</code>        all integer values other than <code>0</code>
1255        are interpreted as boolean <i>true</i> while an integer value of        are interpreted as boolean <i>true</i> while an integer value of
1256        precisely <code>0</code> is interpreted of being boolean <i>false</i>.        precisely <code>0</code> is interpreted as being boolean <i>false</i>.
1257      </p>      </p>
1258      <p>      <p>
1259        So the logical operators shown above always look at numbers at a whole.        So the logical operators shown above always look at numbers at a whole.
# Line 1110  end on Line 1300  end on
1300      </code>      </code>
1301      <p>      <p>
1302        All these operations yield in a <i>boolean</i> result which could then        All these operations yield in a <i>boolean</i> result which could then
1303        by used i.e. with <code>if</code> or <code>while</code> loop statements.        be used e.g. with <code>if</code> or <code>while</code> loop statements.
1304      </p>      </p>
1305            
1306      <h3>String Operators</h3>      <h3>String Operators</h3>
# Line 1198  RESET_CONDITION(??condition-name??) Line 1388  RESET_CONDITION(??condition-name??)
1388        You should only reset a preprocessor condition that way if you did set it        You should only reset a preprocessor condition that way if you did set it
1389        with <code>SET_CONDITION(??condition-name??)</code> before. Trying to        with <code>SET_CONDITION(??condition-name??)</code> before. Trying to
1390        reset a condition that has not been set before, or trying to reset a        reset a condition that has not been set before, or trying to reset a
1391        condition that has already been reset, will both be ignored by the samlper,        condition that has already been reset, will both be ignored by the sampler,
1392        but again you will get a warning, and you should take care about it.        but again you will get a warning, and you should take care about it.
1393      </p>      </p>
1394            
# Line 1366  end on Line 1556  end on
1556        use the preprocessor instead for such things. And like stated above,        use the preprocessor instead for such things. And like stated above,
1557        there are certain things which you can only achieve with the preprocessor.        there are certain things which you can only achieve with the preprocessor.
1558      </p>      </p>
1559        
1560        <h3>Disable Messages</h3>
1561        <p>
1562          Since it is quite common to switch a script between a development version
1563          and a production version, you actually don't need to wrap all your
1564          <code>message()</code> calls into preprocessor statements like in the
1565          previous example just to disable messages. There is actually a built-in
1566          preprocessor condition dedicated to perform that task much more conveniently for you.
1567          To disable all messages in your script, simply add <code>SET_CONDITION(NKSP_NO_MESSAGE)</code>
1568          e.g. at the very beginning of your script.
1569          So the previous example can be simplified to this:
1570        </p>
1571        <code>
1572    { Enable debug mode, so show all debug messages. }
1573    SET_CONDITION(DEBUG_MODE)
1574    
1575    { If our user declared condition "DEBUG_MODE" is not set ... }
1576    USE_CODE_IF_NOT(DEBUG_MODE)
1577      { ... then enable this built-in condition to disable all message() calls. }
1578      SET_CONDITION(NKSP_NO_MESSAGE)
1579    END_USE_CODE
1580    
1581    on init
1582      declare const %primes[12] :=  ( 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37 )
1583      declare $i
1584    
1585      message("This script has just been loaded.")
1586    
1587      USE_CODE_IF(DEBUG_MODE)
1588      $i := 0
1589      while ($i < num_elements(%primes))
1590        message("Prime " & $i & " is " & %primes[$i])
1591        $i := $i + 1
1592      end while
1593      END_USE_CODE
1594    end on
1595    
1596    on note
1597      message("Note " & $EVENT_NOTE & " was triggered with velocity " & $EVENT_VELOCITY)
1598    end on
1599    
1600    on release
1601      message("Note " & $EVENT_NOTE & " was released with release velocity " & $EVENT_VELOCITY)
1602    end on
1603    
1604    on controller
1605      message("MIDI Controller " & $CC_NUM " changed its value to " & %CC[$CC_NUM])
1606    end on
1607        </code>
1608        <p>
1609          You can then actually also add <code>RESET_CONDITION(NKSP_NO_MESSAGE)</code>
1610          at another section of your script, which will cause all subsequent
1611          <code>message()</code> calls to be processed again. So that way you can
1612          easily enable and disable <code>message()</code> calls of entire individual
1613          sections of your script, without having to wrap all <code>message()</code>
1614          calls into preprocessor statements.
1615        </p>
1616    
1617      <h2>What Next?</h2>      <h2>What Next?</h2>
1618      <p>      <p>
1619        You have completed the introduction of the NKSP real-time instrument        You have completed the introduction of the NKSP real-time instrument
# Line 1376  end on Line 1623  end on
1623        Which provides you an overview and quick access to the details of all        Which provides you an overview and quick access to the details of all
1624        built-in functions, built-in variables and more.        built-in functions, built-in variables and more.
1625      </p>      </p>
1626        <p>
1627          You might also be interested to look at new <i>NKSP</i> core language
1628          features being added to the latest development version of the sampler:
1629          <a href="real_unit_final/01_nksp_real_unit_final.html">
1630            Real Numbers, Units and Finalness ...
1631          </a>
1632        </p>
1633    
1634    </body>    </body>
1635  </html>  </html>

Legend:
Removed from v.3119  
changed lines
  Added in v.3817

  ViewVC Help
Powered by ViewVC