LMUSe command line version Documentation

12/05/98
david sharp 
dsharp@interport.net

'LMUSe homepage' is:
     http://www.geocities.com/Athens/Academy/8764/lmuse/lmuse.html
----------------------------------------------------------------

c o n t e n t s

The Command Line

L-Systems (general) 
	Context Sensitivity and Stochastic Rules 
Symbol Alphabet 
	Commands specific to music
	Direction commands 
	Movement / Play commands 
	State stack commands 
	Increment and Decrement commands
	Colors and intrument timbre commands
Rules Files Format
Map Files
Scales 
Rules Files Compatibility 
Troubles 
Credits 

----------------------------------------------------------------
The Command Line

use:
    LMUSe [-sS] [-iI] [-rR] [-mM] [-t] rulefile

where:

     -sS: S is the number of kilobytes for production space (default is 2048)
     -iI: I is the filename of a map file for LMUSe (default is 'LMUSE.INI')
              (see 'Map Files')
     -rR: R is an override for the number of recursions to perform
     -mM: M is a number of mutations. Mutated rules are saved to rulefile.lm
     -fF: F names the MIDI file to be created (.MID extension assumed)
     -t:  split up tracks of saved MIDI files by program.
     rulefile is the name of the rulefile to use. Note that mutating
              a file which already has an "lm" extension will overwrite
              the original rulefile.

-sS, -iI, -rR, -mM, -fF, and -t are optional parameters.
rulefilename is a required parameter

For example:
        LMUSe -iLMUSE.INI -fTest -r5 -m3 -t mylsys01.L

tells LMUSe to read the l-system file 'mylsys01.L', mutate it
3 times (-m3), perform 5 recursions of the rules (-r5), interpret
the results according to the map in the file 'LMUSE.INI'(-iLMUSE.INI),
and put the result in the MIDI file 'Test.MID' (-fTest), splitting up
multi-instrument tracks into separate tracks (-t).

Messages sent to the screen, are also stored in a file called
'LMUSE.LOG' which is overwritten with each run of LMUSe.

----------------------------------------------------------------
L-Systems

L-systems were invented by Aristid Lindenmayer in 1968 to model the development of living organisms. L- systems provide a concise grammar for generating fractals like von Koch snowflakes, dragon curves, as well as for modeling organic growth.

An L-system starts in generation 0 with a simple string of symbols, and for each subsequent generation, symbols are replaced by new symbols or strings of symbols according to a set of rules. The idea is easiest to see by example. Say our starting string, (called the 'axiom'), is just 

	A 

The replacement rules show how each symbol in the current generation should be replaced. Two simple rules (for example) are 

	A=B 
	B=BA

According to the first rule, every 'A' in the current generation should be replaced by a 'B', and each 'B' in the current generation will be replaced with 'BA' in the next generation. 

	generation 0: A (the axiom) 
	generation 1: B (the A was replaced with B ) 
	generation 2: BA (the B of generation 1 was replaced with BA) 
	generation 3: BAB (the B of gen 2 replaced with BA and the A replaced with B)
	generation 4: BABBA 

Note how the replacements for each generation occur in 'parallel'. We will call the number of generations the "recursion level". When the symbols are interpreted as graphics commands, reading from left to right, the resulting pictures can get very complex. In most L-system implementations the symbol 'F' is interpreted as a command to "draw a line". Because of the recursive way the production string is made, L-systems are a concise way to generate the 'self-similar' nature of fractals. 

The purpose of LMUSe is to interpret the resulting symbol strings as musical directions. For example, 'F' is interpreted as "play the note". See 'Symbols'. LMUSe starts with a 'rule file' which contains an axiom string, a set of replacement rules, a default recursion level, and another parameter the 'angle'. 

	#=== Sample Rules file ==== 
	5 #recursion level 
	10 #angle 
	AB #axiom 
	A=+FB # first rule 
	B=AF # second rule 
	#=========================== 

In LMUSe, '#' is a special 'comment' symbol. LMUSe ignores everything on a line after a '#'. The rules are applied to the axiom to make a 'production string' (the first generation). The rules are then applied to that production string in turn, making a new production string (the second generation), which is run through the rules, the process repeated "recursion level" number of times. The result at the end is just called the "production string".

By itself, the production string is just a string of symbols. The production string is then 'interpreted'. That is, the symbols are mapped to drawing and/or music playing commands. In LMUSe, the musical commands are in the form of MIDI messages. 
----------------------------------------------------
Context Sensitivity and Stochastic Rules

Rules like A=AB, and X=FB^&F, are 'context-free' because the symbol to be replaced (on the left of the '=') is replaced by the right side of the '=' no matter what other symbols are around. 'Context-sensitive' rules are rules whose application depends on the surrounding symbols. In LMUSe, context- sensitivity of a rule is with '>' or '<' directly after the symbol to be replaced. 

For examples: 
	A>B=AB     # "if B is to the immediate right of A replace the A with AB". 
	A<F=FFAB # "if F is to the left of A, replace the A with FFAB" 

In LMUSe rules, the symbol to be replaced is always the first symbol in the rule's line. If the above pair of rules were in the same rules file and both conditions were true, (for example if the production contained the sequence "FAB") only the first rule would be actually applied. This is because once a rule is applied to a symbol, all following rules for that symbol are ignored. Also, "A is to the left of B" is True if all that separates A and B is a number or a number in parentheses, or, of course, if A is directly adjacent to B.

To combine both '<', and '>' in a single rule condition, the two conditions must be in that order:

                   A<B>C=X 

would match BAC and replace the A with X. 

LMUSe also implements 'stochastic rules'. 

	A(.5)=BBA 

will replace A with BBA only 50% of the time. The pair of rules: 

	A(.5)=BBA 
	A=ABA 

will replace A with BBA 50% of the time, otherwise (the other 50% of the time) A is replaced with AAB. The order of these two rules is important. Reversing them: 

	A=ABA 
	A(.5)=BBA 

will always replace A with ABA. This is because the first (A=ABA) is not a stochastic rule (no probability in  parentheses) and since it comes first, it is applied. Once a rule is applied to a symbol, the rest of the rules for  that symbol are skipped, so the second line has no effect at all. 

To have three stochastic rules each with a probability of 1/3, you would write: 

	A(.33)=BBA 
	A(.5)=ABA 
	A=BABB 

The way it works is, the probability in parentheses is the probability that the rule will be applied if LMUSe gets to that rule, and LMUSe stops looking for a rule to apply once it has applied one. That is, there is a .33 probability that the first rule will be applied. If it isn't applied (probability .67), then the second rule is attempted. Half the time the second rule is attempted, it is applied. That is 1/2 of .67 =.335 probability that the second rule will be applied. And finally, if and only if neither of the first two are applied, the last rule is applied. Again, once a rule is applied to a symbol, following rules are skipped. 

To combine a stochastic condition with a context-sensitive condition, the stochastic condition must come at the end: 
	F<A>B(.5)=FF

Stochastic rules have their random effect as the production string is being built. Once the production string is done you would have to do another 'Make' to see any difference due to the randomness. 

LMUSe transformation rules can contain another type of randomness. The '~' (tilde) changes, randomly, the current state's direction. And '~(x)' changes the direction randomly up to a maximum of x degrees. This random change occurs at the time of interpretation and will give (probably) different results every time the production string is interpreted.

---------------------------------------------------------------
The LMUSe Symbol Alphabet
(Much of this list quoted from Laurens Lapre's lparser.txt)

Commands specific to music 
-------------------------------------
t(x)  transpose up (+x) or down (-x) by x semitones 
t     transpose 0
d(x)  multiply note durations by x
d     multiply note durations by 1.0
      (cancels d(x))
v(x)  multiply note velocities by x 
v     multiply note velocities by 1.0
      (cancels v(x))
*(x)  write to MIDI channel x
*     write to MIDI channel 1

m(x)  interpret using map number x
--------
(also see the T(x), D(x), and V(x) stack commands)


Turtle Direction commands
-------------------------
+     turn left around up vector 
+(x)  turn x degrees left around up vector 
-     turn right around up vector 
-(x)  turn x degrees right around up vector 
&     pitch down around left vector 
&(x)  pitch x degrees down around left vector 
^     pitch up around left vector 
^(x)  pitch x degrees up around left vector 
<     roll counter clockwise
      around forward vector
<(x)  roll x degrees left
      around forward vector
>     roll clockwise around forward vector 
>(x)  roll x degrees clockwise
      around forward vector
|     turn 180 degrees around up vector 
%     roll 180 degrees around forward vector 
$     roll until horizontal 
~     turn/pitch/roll in a random direction 
~(x)  turn/pitch/roll in random direction to a
      maximum of x degrees

Movement / Play commands
------------------------
F     draw full length forward 
      play 
F(x)  draw x length forward 
      play
Z     draw half length forward 
      play 
Z(x)  draw x length forward 
      play
f     move forward full length, draw within {} 
      play within {}, otherwise rest 
f(x)  move forward x, draw within {} 
      play within {}, otherwise rest 
z     move forward half length, draw if within {}
      play within {}, otherwise rest 
z(x)  move forward x, draw if within {} 
      play within {}, otherwise rest 
g     move forward full length 
      rest 
g(x)  move forward x 
      rest 
.     don't move

State stack commands
--------------------
[     push current state but not event time 
]     pop current state but not event time 
{     push current state and time 
{x    push current state and time,
      writing to MIDI channel x
}     pop current state and time
\     push event time
\x    push event time,
      writing to MIDI channel x
/     pop event time

T(x)  ** push pitch transposition by x onto transpose
      stack 
T     ** pop transpose amount from transpose stack. 
D(x)  ** push duration multiplier x onto the duration
      multiplier stack 
D     ** pop duration multiplier from duration
      multiplier stack 
V(x)  ** push velocity multiplier x onto the velocity
      multiplier stack 
V     ** pop velocity multiplier from velocity
      multiplier stack

** To use the transpose, and duration and velocity
   multiplier stacks they must be enabled by a map
   file parameter (transposestack=1, factorstacks=1).

Increment/Decrement commands
----------------------------
"     increment length (times 1.1) 
'     decrement length (times 1/1.1) 
"(x)  multiply length with x
'(x)  multiply length with x
;     increment angle (times 1.1) 
:     decrement angle (times 1/1.1)
:(x)  multiply angle with x
;(x)  multiply angle with x
?     increment thickness (times 1.4) 
!     decrement thickness (times 1/1.4)
?(x)  multiply thickness with x
!(x)  multiply thickness with x

Color / timbre-instrument commands
----------------------------------
c     increment color index 
      increments instrument program number 
c(x)  set color index to x 
      sets instrument program number to x 



@     end of rules (optional)

---------------------------------------------------------------------------------------------
Rule File Format
---------------------------------------------------------------------------------------------
The format for rule files follows the format of Lparser. Each rule file
must be organized in this order, line by line:

	recursion level
	angle (in degrees)
	thickness (optional in LMUSe, required by Lparser)
	axiom
	transformation rule 1
	transformation rule 2
 	more rules, one to a line
	...
        @ (end of rules marker, optional in LMUSe, required by Lparser)

In addition, the file can contain comments.  Anything after "#" on a line
is ignored by LMUSe.

An example:

	# this whole line is ignored
	4		# default recursion level
	25		# basic angle of 25 degrees
	50		# thickness is 50% of length
	A      		# the axiom 
	A=+B^FA	# rule 1
	B=BBt(12)F	# rule 2
	@		# end



==========================================================================

Map Files
---------------
A 'map' file is a file that contains the map parameters. 'Map parameters' tell LMUSe how to interpret the production string as music.
The format of these files is a list of "parameter=value" statements. The hash ('#') is used as a comment starter (everything on a line after a hash symbol is ignored).
An example file is LMUSe.INI:

      # LMUSe.INI
      # Default initialization file for LMUSe command line version
      
      # The letter case in the INI file is not important.
      # Everything to the right of '#' on a line is ignored
      
      # 'scale' tells LMUSe what scale to use.
      # the scale choices are 'Major', 'minor', 'Blue1', 'Penta1',
      # 'diminished', 'Twelvetone', 'whole', or a list of halfsteps up a
      # scale starting with the number '1'. For example, a major scale
      # could be given by the line:
      # scale=1,3,5,6,8,10,12
      scale=Major
      
      # scalefn is the algorithm used to get the pitches to conform to the
      # desired musical scale. (see LMUSe.txt)
      # Choices are: slideto, steps, constant, ignore
      scalefn=slideto
      
      # tempo is in beats per minute
      tempo=120

      # the pitch, duration, and volume of notes are each determined by
      # any one of the following L-system variables:
      # x          - the x coordinate of the turtle's position
      # y          - the y coordinate of the turtle's position
      # z          - the z coordinate of the turtle's position
      # forwardx   - (or 'fx') the x component of the turtle's forward direction
      # forwardy   - (or 'fy') the y component of the turtle's forward direction
      # forwardz   - (or 'fz') the z component of the turtle's forward direction
      # leftx      - (or 'lx') the x component of the turtle's 'left' orientation
      # lefty      - (or 'ly') the y component of the turtle's 'left' orientation
      # leftz      - (or 'lz') the z component of the turtle's 'left' orientation
      # upx        - the x component of the turtle's 'up' orientation
      # upy        - the y component of the turtle's 'up' orientation
      # upz        - the z component of the turtle's 'up' orientation
      # length     - the state length of the L-system
      # drawlength - the 'draw length' (affected by temporary length modifiers)
      # thickness  - the Lparser line thickness
      pitch=x
      volume=lefty
      duration=drawlength
      

      # pspread is a magnifier for the influence of the pitch parameter
      # on the pitches generated. Recommended to stay in the interval
      # -4 to 4
      pspread=1
      
      # dspread is a magnifier for the influence of the duration parameter
      # on the pitch durations generated. Recommended to stay in the interval
      # -6 to 6
      dspread=1
      
      # vspread is a magnifier for the influence of the velocity parameter
      # on the velocities (loudness of note) generated. Recommended to stay
      # in the interval -6 to 6
      vspread=1
      
      # transposestack is a special stack for transposing the generated
      # pitches. (L-system symbols T(x) ... T).
      # transposestack=1 enables this stack (default is OFF).
      transposestack=0
      
      # factorstacks are special stacks for holding duration multipliers
      # and volume multipliers. (L-system symbols D(x) ... D ,
      # and V(x) ... V).
      # factorstacks=1 enables these stacks (default is OFF).
      factorstacks=0

=================================================
Using Musical Scales

The different predefined scales are described below. 

`User' choice is for defining your own scale. If you pick `User', you are given a box where you type in some scale steps and the scale you type in will be used. The scale you type in should start with  '1' (lowest note of the scale) and thereafter each following number denotes the next higher note in the scale, in 'half tones'. Put spaces or commas between different scale notes. For example: 

        1 3 5 6 8 10 12 

would make a major scale. 

The scales in the list are defined, either by convention or by me, as:

twelve tone
1 2 3 4 5 6 7 8 9 10 11 12
c c# d d# e f f# g g# a a# b 

Major 
1 3 5 6 8 10 12 
c d e f g a b 

Penta 1
1 5 8 10 11
c e g a a# 

minor 
1 3 4 6 8 9 12
c d d# f g g# b 

blues 1
1 3 4 5 8 9 10 11
c d d# e g g# a a# 

whole tone
1 3 5 7 9 11 
c d e f# g# a# 

diminished
1 2 4 5 7 8 10 11
c c# d# e f# g a a# 

----------------------

Scale Function

LMUSe gets note pitches from some L-system state variable, as chosen by the 'pitch=','duration=' and 'volume=' lines in a map file. These variables are actually numbers. If you want to use a scale, those numbers need to end up being notes in the scale. LMUSe lets you choose between two (three) ways to do this. 

The `slideto' setting finds the lowest note greater than or equal to the generated number. It finds the next note by going 'up' the scale until reaches the number or greater. It uses semi-tones to step up.

`steps' uses the generated number to count directly up the scale. That is, it uses the scale itself to step. 

`constant' is just a way to get a constant pitch rather than a scale. 

=================================================
Rules Files Compatibility

Laurens Lapre's Lparser *.LS files should all work with LMUSe. On the other hand, LMUSe files do not have to work in LParser. LMUSe rule format can contain context sensitivity and stochastic syntaxes which LParser is not designed to parse. The LMUSe parser was designed to interpret the large number of LParser files available. On the other hand, while LParser can make beautiful graphics, LMUSe can't. The drawing that LMUSe does while interpreting is intended mainly as a 'progress indicator'. LMUSe is for generating music and it is just fortunate that some of the LParser files do make nice music when fed to LMUSe.

I have tried to keep the file extensions of the examples consistent. A file with just an 'L' extension is an LMUSe rules file which does not contain a 'thickness' (the third non-comment in Lparser files is the starting thickness of lines as a percent of their length), or if it contains stochastic rules (a la A(.5)=ABB) or context sensitive rules (e.g. A<F=BF). If I believe a file is compatible with Lparser (for example, contains a thickness), it has the 'ls' extension. A file that has been mutated by LMUSe (saved after 'mutating') is given the 'lm' extension. 'lm' files which were originally 'ls' files before mutation should be compatible with Lparser, but I haven't checked all of them.

Certain of the symbols are interpreted differently in the two programs. For example 't(x)' in LMUSe transposes the following notes by x semitones. In LParser, t(x) is interpreted as a 'tropism' or gravity influence. This interpretation of 't(x)' is completely ignored by LMUSe. In LMUSe, '{' and '}' are mostly for creating parallel in time (polyphonic) musical lines. Their use in LParser is for making polygons like leaves, flower petals, etc). LMUSe does not know 'polygons' at all and so the generated graphics often look different from what you might expect from seeing the same file interpreted by LParser. 

Another popular program for doing L-systems is Fractint. Fractint L-system files (*.l) are 2-dimensional. To use them with LMUSe requires some easy adaptations to the Fractint file. First, a Fractint 'L' file can contain many sets of L-system rules. The different L-system rule sets have to be isolated in their own file to be used in LMUSe. Fractint L-system rules, parameters and axioms are also enclosed in curly brackets. You have to erase those. Plus you have to get rid of the labels like 'Angle' and Axiom'. and insert a recursion level on the first line. Also, the comment character in LMUSe is '#' while in the Fractint .L files it is ';'. And the angle in the Fractint files is a division of 360 degrees rather than the number of degrees itself. One other major difference in symbols is that @nnn in Fractint systems is the command to multiply line segment lengths by nnn, while the corresponding LMUSe command is "(nnn). (a double quotation mark followed by the multiplier).

Also note that Fractint's interpretation of .L files is case insensitive (e.g. F'='f'). LMUSe is quite sensitive to case. (e.g. 'F' and 'f' are interpreted differently).
There are a number of other differences which I guess I am not prepared to detail.

For example (grabbed from fractint.l):

        Koch1 { ; Adrian Mariano
        ; from The Fractal Geometry of Nature by
        ; Mandelbrot
        Angle 6 
        Axiom F--F--F F=F+F--F+F }


would become (koch1.l):

        # Adrian Mariano 
        # from The Fractal Geometry of Nature
        # by Mandelbrot
        3 # recursion level 
        60 # 60 degrees = 360 degrees/ 6 
        F--F--F # Axiom 
        F=F+F--F+F 


Most of the more symmetric L-systems make static/boring music. One thing that `helps' such files is changing the basic angle. Naturally this will `screw up' the picture, but often gives the music more life. 

=======================================
Trouble
If you just can't get LMUSe to run (crashing on start up), please let me know (dsharp@interport.net). I have prepared fixes for some individual situations.

Running out of production string space: 
The default limit for production string space is 2 megabytes. You can increase this limit on the command line with the parameter "-sXXXX" where "XXXX" is the number of kilobytes of string space to allocate. For example, "-s4000" will allocate room for 4 megabyte production strings. Be aware, however, that LMUSe actually allocates for two strings of this size. That is, -s4000 will actually allocate for two 4 megabyte strings. Since LMUSe can use DPMI virtual memory, allocating large amounts should not be a problem except for speed concerns. 

Running out of stack space: 
If you are finding that the stack space is often too small, let me know. Running out of stack space is most likely to occur when trying to run a mutation. The real remedy is to increase the stack space by recompiling LMUSe with a larger stack size. (The current size is 2K). A workaround to try is to reduce the recursion level you are attempting, if the recursion level is not critical to your project.

Arithmetic errors: 
A mutated set of rules sometimes causes an arithmetic error during interpretation. Usually this is because the 'drawing' was growing without bound. In such a case, LMUSe currently just quits with a notice of what happened. However, f LMUSe actually crashes due to an arithmetic/floating point error, please send me details. 

Sequencer chokes on LMUSe MIDI files:
Try using the '-t' command line parameter to split the tracks of the saved MIDI files by instrument.

=================================================

Credits

Special thanks to: 

D J Delorie for DJGPP v2 
DJGPP web site 

Laurens Lapre for LParser.
ljlapre@xs4all.nl 
http://www.xs4all.nl/~ljlapre/

Tim Thompson and Michael Czeiszperger for the MIDI file output routines in`midifile.c'

End of File



