========================================================================== New Features in nGEN 2.1 (May 2010) ========================================================================= Added two new global constant shortcuts: @3 is like @1 but evaluatates in BEATS (not seconds like @1) @4 is like @2 but evaluatates in BEATS (not seconds like @2) ========================================================================== New Features in nGEN 2.0 (February 2006) ========================================================================= nGEN 2.0 contains many new features as well as bug fixes for version 1.0. ************************************************************************** <> ************************************************************************** Format: pf(scalar) where "scalar" is a floating-point value to apply to parameter data. Restrictions: PF can only be used in the i-block (but not in P2). It can be used in P3 also; but be careful as negative (-) values will cause rests to be created! PF takes affect exactly where it occurs in a p-field and will be in affect until the beginning of the next p-field. For example: p4 pf(2.5) mo(T 1. 0 10) will rescale the "move" contents to 0 to 25. ************************************************************************** <> ************************************************************************** Format: pf(scalar) ************************************************************************** <> ************************************************************************** The z variable is a shortcut notation for "the last thing generated". It can be used within any DDF or in normal input mode. If you are using random deviation (rd) it will be applied to the z variable. For example if you had: p7 rd(.5) 4 z the z would stand for "4" and would be altered by RD before being stored in the note list. "Z" is particularly useful in DDFs. For example, p5 no ra(T*.3 1. [c2 ef6]) mo(T*.7 1. z c4) will create a wide range of random pitches for 30% of the i-block duration. The "z" variable is then used to catch the last value generated (by RA, whatever it might be...) and will then move to middle C over 70% of the i-block duration. (The z variable, which can also be uppercase, is initialized to 0 at the start of the *i-block*. It always represents the last thing made (and will even pull a value from the end of a previous p-field -- so be careful :) ) ************************************************************************** <> ************************************************************************** This new input mode forces any stored data (including those made by DDFs) to round to the nearest integer (although these are still stored internally as floats). p6(xx) se(T 1. *[0 1 2 3 4 5 6 7 8 9 10 11]) ;PCs p7(xx) in rw(T 1. [1 7] .25 4) ;Octave #s p5(op) ex(T 1. i[p25*12+p24]) ;Pitches (PCs+OCs) In the above example, p5 and p7 are used as scratch p-fields ************************************************************************** << DDF: The HO (hold) command >> ************************************************************************** Format: ho(time value) This is a short-cut for holding a single value and is essentially the same as: "mo(time 1. val val)" or "ra(time 1. val)". For example: ho(T 54.2) is the same as "mo(T 1. 54.2 54.2)" or "ra(T 1. 54.2)" but is more intuitive and involves a little less typing. Consider the following for generating a "sample hold" process: p4 ra(T*.1667 1. [0 100]) ho(T*.1667 z) ra(T*.1667 1. [0 100]) ho(T*.1667 z) ra(T*.1667 1. [0 100]) ho(T*.1667 z) ************************************************************************** << DDF: The S2 (Sets 2) command >> ************************************************************************** Format: s2(time percentage set1 percentage set2 ...) Where set1 can be a single value, E or [E E E E ... ] This is exactly like the SE command however it can include fused data (chords). **It can only be used in p-fields > p3.** For example: p4 s2(T .5 48:52:60:63 .5 [55:56:57:58 49:56:64:71 27 38:45:67]) OR p4 no s2(T 1. [c4:e:b f3:cs4:e d3:g3:ef4:fs af5]) This will function exactly like the SE command (it will choose among the listed elements). When fused data (chords) is encountered, the original event will be copied and duplicated in the note-list (the data from previous p-fields will be copied to the new events.). @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ************************************************************************** << DDFs SE and S2: New Generative Methods>> ************************************************************************** Two new generative methods have been added to the SE and S2 DDFs format: SE(time % o [set1] % o [set2] ...) where the optional "o" stands for "x" or "*". x: If the "x" flag is given between the % and [, the following set will be made with no contiguous repetitions. *: If the "x" flag is given between the % and [, the following set will be cycled through without repetitions (until the cycle has ended). This could be used to create Carter style "aggregate completion" and is similar to Max/MSP's "Urn" object. EX: se(T .5 *[0 1 4 6] .5 x[7 8 2 3]) The first set (0146) will be cycled through one element at a time until the whole set is used, then it will start over. The second set (7823) will be chosen from randomly without contiguous duplications. If the * or x is loft off, it will simple function in the manner of version 1.0 (random selection). ************************************************************************** << DDF EX and new "type casting" methods>> ************************************************************************** format: EX(time % o [equation1] % o [equation2] ...) OR EX(time % o pn % o pn ...) where the optional "o" stands for "i" or "f". A new typecasting method has been added to the EX command. If "o" = "f" it will return a floating-point number. If "i", it will round the internal value to the nearest integer. ************************************************************************** << Macro Arguments >> ************************************************************************** In version 1.0.0, macro argument names had to be separated by a ' or a # (ala Csound). In this release, I've added the "," to definitions. SO, #define B(rseed, p3data, dur). This format WILL NOT for macro calls, however, because you might want to do something like this: $B(392'se(T, 1. [0 1 4])'30) So you can see how that might make for some problems. :). ************************************************************************** << Numeric Macros and Expressions >> ************************************************************************** Variables (numerical macros) Parsed as preprocessor directives (like macros) *after* regular (textual) macros (i.e., a textual macro can include a variable). For example, #const Interval 10 ; time between blocks #define NEXT #~[@2 + Interval]# So that $NEXT can be placed anywhere but will not be expanded in the textual macro definition (but rather in the final macro-expanded file). TO DEFINE A NUMERICAL MACRO: #const LABEL VALUE (e.g., #const END 22.45) where LABEL can be any text string that begins with a letter and can contain any alphanumeric character including the '_' after the first character. This is just like textual macros. CALLING NUMERICAL MACROS There are two ways to call macros (the ~ (tilde) flags them): A) as a name: ~LABEL (e.g. ~END) B) as an equation (e.g.: ~[A o B o C o D ...]) EQUATIONS In the equation syntax, A-D represent "terms". A term can be a *floating point number*, a *previously declared variable name*, or a *special token*. Equations must be on the same line. OPERATIONS These are identical to the "EX" DDF. The available operations are +, -, *, /, and %. IMPORTANT: the order of operation is from *left to right*. For example: ((((A o B) o C) o D) o E). So, 7 + 2 * 3 = 27 and 2 * 7 + 3 = 17. SPECIAL TOKENS There are three special tokens, "@1", "@2", and "?": "@1" is a shortcut value for the "previous i-block duration". This is the "actual" duration as opposed to the end time stated in the i-block header. This time is the last event in the i-block (plus its duration) minus the start time (i.e., the time from the start of the first event in the list to the end of the last event). "@2" is the cumulative time -- the "actual" total time of all i-blocks created from time "0" until the end of the last event of the last i-block. "?" represents a random floating number between 0 and 1. N.B. At the moment numerical macros will always evaluate to a floating- point number with a decimal-point precision of 3. This will make for some problems if you have to use them as integers. In some cases, since the variable evaluation is preprocessed you can get around this by using the "IN" input mode. You can't, however, use a variable in the i-block header for the number of events. :( ************************************************************************** << Tempo changes with TE >> ************************************************************************** The TE command has been expanded and now allows for static tempos (ala version 1.0) AND tempo changes. (This one's for you Allan!) How Tempos Work: Tempos can be defined in two ways: 1) a generic tempo FORMAT: te(f), where f = a floating point value listing the tempo 2) a dynamic definition (a tempo change) FORMAT: te(start-beat, end-beat, tempo1, tempo2, type) where "type: is: F = Flat (linear) E = exponential L = logarithmic (inv. exponential) S = sine C = inverted sine Vn = User specified variable curve (based on exponents) V1 = linear (F) V2 = exponential (E) V.5 = logarithmic (L) V3 = steep exponential V.25 = steep logarithmic (L) N.B. beats start from 0. Dynamic tempo changes need not be listed contiguously; if a subsequent definition doesn't start where the previous one ended the end tempo of the previous definition will be held. If two definitions coexist with conflicting times: E.G. te(10, 20, 90, 60, e) and te (15, 30, 120, 120) The second one will override the first at its start time (15) (the tempo in this example will be 60 until beat 10, then move from 90 to 60 through beat 20. This, however will be interrupted and, at beat 15, the tempo will be reset to 120. Tempos can only be defined in the g-block and will *affect all i- blocks* thereafter, unless a new tempo definition (or set of definitions) are listed. If definitions conflict: The second definition will overwrite the first starting at the second definition's start time. If an i-block is defined between two definitions, the tempo list is reset (any previously defined changes will be erased completely if a new tempo definition is found after the current i-block is processed) and this new definition is used for subsequent i-blocks (or until a new definition is found). If there is no tempo definition, the tempo defaults to 60 bpm. There can be up to 50 tempo changes in effect at once. The statement te(x) will clear ALL tempos from memory. ************************************************************************** << -t Option >> ************************************************************************** When invoked with the "-t" option, nGen will print verbose information about tempo changes to the console. ************************************************************************** << -s Option >> ************************************************************************** When invoked with the "-s" option, nGen will suppress the printing of all non-error (non-fatal) messages to the console. ************************************************************************** << New DDF Curve and Generation Types >> ************************************************************************** The MO, MS, and TE commands now have additional curve type specifiers. The following are all possible specifiers in version 2.0: F = Flat (linear) (Version 1.0) E = exponential (Version 1.0) L = logarithmic (inv. exponential) (Version 1.0) S = sine (0-90 degrees) C = inverted sine (i.e. a sine from 180 to 90 degrees) Vn = User specified variable curve (based on exponents) V1 = linear (F) V2 = exponential (E) V.5 = logarithmic (L) V3 = steep exponential V.25 = steep logarithmic (L) V value Half-data point (0-1) (approx.) .2 .87 (log) .25 .84 ( " ) .5 .7 ( " ) 1 .5 (lin) 2 .3 (exp) 3.5 .085 ( " ) 5 .03 ( " ) 7 .008 ( " ) 10 .001 ( " ) In addition, the MO and MS commands have a new flag that will create a temporal data change based on the current position in time. In version 1.0 all "moves" (MO and SE) were based on the current index within a total number of events. In version 1.1 you can opt for a MO or MS that interpolates between two values based on the current position in beats. N.B. This feature only works when the i-block duration is in beats (not events). For example: mo(T*.5 1. V3 * 1 100) ^ this specifies to calculate the current value based on the current temporal position in the move, rather than the current event in the move. will move for have the i-block duration (beats) from 1 to 100 with a steep exponential curve. The values created will be made according to their position in time (not the current event number). This feature will not work in P2 since start times are always created with respect to their position in time (since there are no other events in the note list when they are created). ************************************************************************** << P3 Code Notes >> ************************************************************************** In version 1.0 all P3 codes worked in relation to the tempo (beats). Since tempos were always constant, it was a bit nebulous as to whether or not they worked off beats or seconds (absolute time). In version 1.1 all P3 codes also work in relation to the tempo; however, this makes a few things awkward (for example if you want a particular duration in P3 you would use the 400 + duration code. This, however, will cause for unequal durations if the tempo changes.). Version 1.1 has a new code: 1000 + duration. The duration here is in SECONDS (e.g., 1003.5 would be 3.5 seconds). ========================================================================== Bug Fixes in nGEN Version 2.0 (i.e., Bugs in 1.0) ========================================================================== 1) When the "}" character occurred as the last character in the file there was an error. This has been fixed. 2) When C-Style comments occurred outside of the i-block, there was some times an error message after the "*/". This has been fixed. 3) At times, a p-field with a succession of DDFs, would give a warning message that data had been truncated. The warning message would get stuck in a nasty endless loop. This has been fixed. 4) In macros, it wasn't possible to use line comments (;) -- although C-style (block) comments (/* */) would work fine. This has been fixed. 5) In this release, I've added an error trap that looks for garbage between command names and their ()s (ex ra().) and prints an error message. 6) In version 1.0.0 you couldn't have a macro definition that started with an i, such as: #define X #i1 = 3 0 30 p2 1 p3 1 # Now you can do this. (This was seen as a possible include file and the "i" was skipped if it occurred after the #). 7) When processing higher numbered p-fields out-of-numerical order, the higher p-fields where not processed correctly with fused data. For example, i1 = 7 0 5 { p2 2 p3 1 p4 4000 p6(in) <1 2 3> p7(in) <1 2 3> p5 no c2:cs1:gf3:g2x3 } should copy in the <1 2 3> values to the fused data so that all events with the same start time have the same p6 and p7 data. In this example, reversing the p-fields is useful because it allows us to ensure that each "chord" has the same values in p6 and p7. If we were to make put the p-fields in numerical order, P6 and P7 would be processed after p5 and would have different values for each "note" in each "chord". This now works properly. 8) It was possible to leave out the i-block duration (e.g. i1=3 0 { ). This was fixed and two error messages were added. 9) If the i-block duration was negative (EVENTS) and fused data was present (chords), the T variable was not reset (e.g., T would not evaluate to the new number of events -- it was still set to the old number). This has been changed -- in get_ddf() the EVENTS global is now referenced instead of EVENTS. If the i-block time is < 0 (events) and fused data changes the internal number of events, a message is now printed after each p-field is processed. (Added a new variable "events" in do_pfield() (parse.c) and a check at the end of the function that prints a message stating the event change). 10) A p-field could be blank, in which case nGen would just hang! This has been fixed: a global variable, WROTE_DATA, was added in 4 places: Store_Pfield() and Insert_BegTime() (notelist.c) and storeP3() (utils1.c). These are TRUE when data is put in the notlist for repective pfields and FALSE when a p-field is initiated. 11) EX DDF: There were a few error-trapping problems concerning the use of p-fields that were not previously define (these are not allowed). Now an error is given. 12) There were no error checks on tempos (they could be < 0, for example); now there are specific restraints built into the new tempo commands. 13) The RD command can now only hold values >= 0 and < 100 (10 thousand %!). 14) In 1.0 there could only be one "#include" directive -- this was a bug and has been fixed so that there may be an unlimited number of "#includes". ========================================================================== CODE CLEAN_UP ========================================================================== 1) There were two definitions for "OUTS" one was an array for output types the other was a #define macro for printing strings -- this one was changed to OUTSTR. 2) There was an error in the get_EX_data function for getting an equation with a single p-value. ========================================================================== MISC. ========================================================================== Several people have asked me how one can enter pitch-class and octave data separately and then combine them into a pitch code in one p-field. This is how you do it: /* This macro will extract a p-field of pitch-classes and a p-field of octave numbers, then combine them into an internal pitch code (e.g., 48 = pc 0 + oct 4) */ #define Fuse(OctavePF'PitchClassPF) #ex(T 1. [$OctavePF * 12 + $PitchClassPF])# i1 = 7 0 -12 { p2 .5 p3 1 p4(in) 15000 /* Scratch P-fields (not printed in output score)*/ p6(xx) se(T 1. [0 1 6 7]) ;PCs p7(xx) se(T 1. [1 2 3 4]) ;Octaves /* extraction from the "scratch" p-fields */ p5(op) $Fuse(p7'p6) } Enjoy! Mikel Kuehn, February 2006