CS_RAND.DOC 8 September 1995 Robin Whittle firstpr@ozemail.com.au rwhittle@ozonline.com.au http://www.ozemail.com.au/~firstpr For Csound users and C programmers. Mods to the xlinrand series of noise generators added by Paris Smarargdis early in 1995. * A new common 31 bit random source for these generators. * Setting the seed of this generator. * A new uniform distribution generator to make the family complete. (Uniform means even distribution between 0 and 1.) Introduction ============ I am interested in a Csound piece having quite a few random elements, and there being: 1 - An even distribution of random values. 2 - A long pattern of pseudo-randomness - ie. not a short repeat pattern. 3 - The ability to start all random functions from a single seed. So ideally, all random functions would return a huge range of values, in a very long repeating pattern, and a single seed would start the process. This enables me to create a piece say ten times, based on ten seeds that I choose. Then I can listen to them and depending on which one I like the most, know that I can regenerate that version predictably from the same .ORC and .SCO file and the same seed. So all random numbers would arise from a single common function, working from a single variable which is updated every call. This variable is initialised by the seed I put in. Ideally, we would be dealing with floating point numbers, or at least 32 bit long integers. The repeat time is typically a direct function of the length of the internal state number. What we have at present (until my mods) in Csound is rather different from that. Standard rand ugens =================== These are rand, rand and randi in ugens4.c. They all share a common approach to generating a pseudo random number. Each instance maintains its own state, so if you have two rand ugens in the one instrument, or in separate instruments (or separate instances of instruments) then if you start with the same seed, each ugen will always produce the same sequence of numbers, at whatever rate it runs. The default is for a seed of 0.5. The 16 bit state is multiplied by a constant (15625) then 1 is added. The result overflows, and the remainder is a 16 bit number, which is treated as a signed short integer (+32767 to - 32768). This is converted to a float and divided by 32768 to give a +/- 1 pseudo-random number. I presume the constant has been chosen wisely to give a repeat cycle as long as possible. Since it is a 16 bit internal state, it must repeat at least every 65536 cycles. This system has no common state which I seek for controlling all pseudo-randomness in a peice from a single seed. I suspect that the sophisication of the random generator is modest. Certainly it has a short repeat cycle. zlinrand family of noise generators =================================== These were added by Paris Smarargdis early in 1995. The doco I use comes from John Fitch's readme.cso 14 April 95 (the original file name on his Bath ftp server was README.csound). Looking at the code in CMATH.C, it can be seen that they all operate from the standard ANSI C library function int rand(), which returns a 16 bit pseudo random number and maintains a 16 bit state. This goes a long way towards what I want. Given a particular seed, rand() will produce a predictable sequence of numbers. I presume its algorithm is a more sophisticated than that used in ugens4.c. In the doco, there is nothing mentioned about setting the seed, but Paris has provided a ugen for it. seed iblah This ugen runs only once, at the init time of the instrument it is in. It sets the C library's rand() internal state to the contents of iblah. It also prints iblah on the console, with three decimal places of precision. Looking at the ANSI C doco and running tests, I have determined the following: 1 - The result and internal state of rand() is based on a 16 bit integer. 2 - The seed ugen feeds its parameter directly to the ANSI srand() function. 3 - This is expecting an integer, not a float. So there is no difference between iblah being 12.7 and 12. This means that the printing of 3 decimal places of precision is misleading - these make no difference. 4 - The seed ugen requires an integer between 0 and 65535. Anything outside that just wraps around. So giving it 0 or 65536 gives the same subsequent set of pseudo-random results. DJGPP (DOS GCC ANSI Compiler) contains an "improved" set of pseudo- random generators random() and srandom(), but they too are based on 16 bit values. These are not, to my knowledge standard ANSI C functions. I will use Paris' set of random noise generators in preference to the standard Csound ones - they share a common seed, allow the seed to be set, and use the ANSI library random number generator. I wish I knew more about some of the distributions provided in this set of ugens. The above comments apply to the original CMATH.C and CMATH.H - now called CMATH.COL and CMATH.HOL in my CSRW_SRC.ZIP. My modifications ================ Here is what I have done in my modified CSMATH.C and CSMATH.H. These mods do not affect the standard Csound random generators - only Paris' xlinrand family - and then only in the quality of the random numbers they generate. 1 - The global random number generator -------------------------------------- I have removed Paris' use of the compiler's rand() function. In its place I have installed a 31 bit function - the source code of which I obtained from a library function of a BSD Unix release which I found via a WWW search. This is but one line from their rand function - a simple self contained function which was only used when some much more elaborate code is not used. It seems that pseudo random generators come in three flavours: 1 - El-cheapo 15 or 16 bit things - quite sus for audio rate noise. 2 - Relatively rare, but still simple 31 bit generators like this one. (This is the only one I could find.) 3 - Much more complex, highly sophisticated generators suitable for use in the most demanding scientific calculations. The new generator is the multiply by foo and add bar kind - where both foo and bar have somehow been chosen to give a very long repeat cycle. The original Csound random number generators rand, randh and randi are not affected by this - they have their own self contained generator for each instance. To set the seed for the new global generator use the seed ugen: seed iblah Where the seed is any number. Values with three decimal places between 0 and 2147483.647 should give unique starting points. Here is the new code for the common random number generator and its seed setting function. /*-----------------------------------------------------------------*/ #define GRAND_MAX 0x7FFFFFFFL /* grand() * * A 31 bit global random number * generator. * * This is intended to give a * long cycle - longer than the * 32767 cycles available * from ANSI rand() - which show * up as cyclic fluctuations in noise * when we use them at audio rates. * * We need a global variable for * the state - a 32 bit long integer. * * Initialize it with an auspicious * number derived from 32 tosses of * a coin. * * Also define the maximum value for * the random number generator - see * start of file. * * The generator produces a 31 bit * positive integer. */ long grandstat = 0x16BA2118L; /* Random number generator. */ long grand(void) { grandstat = (grandstat * 1103515245 + 12345) & GRAND_MAX; return grandstat; } void seedrand(PRAND *p) { /* New random seed setting function. * * Paris wrote it to use the same * PRAND data structure as the other * functions. It has only one input * parameter, and no output parameters * so the input is in the first * variable in PRAND. * * Take this to be a floating point * number with three decimal points. * Display this and multiply it by * 1000 to make these parts of the * number map into the final 32 bit * seed. * * This is quite arbitrary, but it * enables people to use numbers * between 0.000 and 2147483.647 * to get unique starting points. * * Hash the seed with another * auspicious number before using * it. This number comes from the * xor of 32 tosses of two coins - * and rounded to an odd number so * bits 0 to 30 of the original * value affect their respective bits * of the final 31 bit seed. * * Unique starting points can be * had from any number with three * decimal places of precision from * 0.0 to 2147483.647. */ printf("Seeding with %.3f\n", *p->out); grandstat = ( (unsigned int)*p->out * 1000) * 0xA7DD150D + 1234567; grandstat = grandstat & GRAND_MAX; } /*------------------------------------------------------------------*/ 2 - New Uniform random number generator --------------------------------------- A uniform random number generator has an even distribution of numbers between 0 and 1. It is a standard, no frills random generator. (The linear distribution generator has a high probability of it being near 0 and a decreasing probablility of the number being higher - until there is a virtually 0 probability of it being near 1.) New ungens: ir iunirand irange kr kunirand krange ar aunirand arange These are like the rest of Paris' family of noise generators. The output value is xrange is multiplied by the raw 0 to 1 random number. Paris told me he meant to include this in his release. <<<<