UGRW1.DOC A description of the new unit generators of Csound in UGRW1.C and UGRW1.H. 7 September 1995 Robin Whittle firstpr@ozemail.com.au rwhittle@ozonline.com.au http://www.ozemail.com.au/~firstpr For Csound users and C programmers. Short description ================= 1 - Table write ugen - for writing directly into function tables at i k or a rates - with i or k rate control of the table number. (See tabread.doc for krate control of table reading ugens in the new ugens2.c.) Intended to be a lasting part of Csound if most people think it is elegant. 2 - Ugens for manipulating data in tables: tablegpw, tableleng, tablemix and tablecopy. 3 - Ugens for reading and writing to table locations sequentially from a rate variables. 4 - Patching of i, k and a rate signals based on numbers generated at i or k rate. This is really a fudge until the language supports arrays. This is called the "zak" system. 5 - Simple ugens to read the time elapsed since the start of the piece or the initialisation of the instrument. Could be mighty handy. Also intended to be a lasting part of Csound if people like them. 6 - Two ugens for printing k rate variables as numbers on the screen. These, or something like them are intended to become a lasting part of Csound if people like them. 7 - Discussion of why arrays (or in the meantime the "zak" system) are so important for certain applications. 8 - (MSDOS only) List of ANSI screen codes of interest when using printks. See the code for details of the innards of the new ugens - it is well commented. Much of the following is adapted from the comments in UGRW1.C. I intend that comments in the code be totally up-to-date. This file ugrw1.doc is intended to me more complete and readable. This doco is preliminary. If the ugens become well accepted I will write them up in a way similar to the others in the manual. >>> 1 - TABLE WRITE =================== This works on existing function tables, changing their contents. There could be all sorts of uses for this. Assuming that users (.orc and .sco programmers) know what they are doing, then there should be no more trouble than the use of global variables. As when using global variables, the user must consider how the code is run. In each k cycle, instruments are executed, in order of instrument number, and within instruments, in order of the instances of the instrument. I presume the instance order depends on their starting time. As execution proceeds, each ugen is run once at k time. For k type ugens, they do their job once. For a rate ugens, they process one or more arrays of a rate variables. For instance a table read at a rate, with ksmps = 7, uses a 7 long array of indexes to read into a table, retrieving 7 different values and writing them to a 7 long array for the output. So ablah table azot, 5 will read from table 5, a set of values pointed to by an array of indexes pointed to by azot, and write them to an array pointed to by ablah. We may conceive of an idea of writing successive a rate values to a single table location, and subsequently reading them from that location. This would not work with ksmps = 7 - only the last written value would remain by the time execution passed to the next ugen. So table write is a means of patching i or k rate signals to particular locations in function tables, where they can be read by table read ugens. However this does not work for a rate signals, unless you conspire to use a range of the table, and organise your indexes very carefully. Patching of i, k and a rate signals under .orc program control is best achieved with arrays - which do not yet exist. See the zak system for a next best solution with ugens. So the main purpose of a table write ugen is to refashion function tables on the fly under program control. tablemix, tablecopy, tablera and tablewa can also be used for such purposes. Applications are diverse. One is to generate a waveshaping table with .orc code. A loop could be created and an instrument could spend some time with k rate operations looping to address each table location - rewriting the table, before letting performance proceed. This would probably be too slow to work with real-time music production. Another application is to continuously sculpt tables while they are being used. Each k or a cycle, one or a few locations are changed a little. With these applications in mind, lets look at the tablew and itablew code. Firstly , itabelw is just the same as k rate tablew, except it only happens once at the initialisation of the ugen. (I must investigate what happens if an i rate ugen is executed first, via an if goto, some time after the instrument is initialised.) The Csound orchestra loader decides whether this is k or a rate operation, and fires up the appropriate subroutine in the unit generator code. There is no output variable. itablew, tablew and tablewkt ---------------------------- itablew isig, indx, ifn [,ixmode] [,ixoff] [,iwgmode] Use itablew when all inputs are init time variables or constants and you only want to run it at the initialisation of the instrument. tablew is for writing at k or at a rates, with the table number being specified at init time. tablewkt is the same, but uses a k rate variable for selecting the table number. The valid combinations of variable types are shown by the first letter of the variable names: itablew isig, indx, ifn [,ixmode] [,ixoff] [,iwgmode] tablew ksig, kndx, ifn [,ixmode] [,ixoff] [,iwgmode] tablew asig, andx, ifn [,ixmode] [,ixoff] [,iwgmode] tablewkt ksig, kndx, kfn [,ixmode] [,ixoff] [,iwgmode] tablewkt asig, andx, kfn [,ixmode] [,ixoff] [,iwgmode] isig, ksig, The value to be written into the table. asig indx, kndx, Index into table, either a positive number range andx matching the table length (ixmode = 0) or a 0 to 1 range (ixmode != 0) ifn, kfn Table number. Must be >= 1. Floats are rounded down to an integer. If a table number does not point to a valid table, or the table has not yet been loaded (gen01) then an error will result and the instrument will be de-activated. ixmode Default 0 ==0 xndx and ixoff ranges match the length of the table. !=0 xndx and ixoff have a 0 to 1 range. ixoff Default 0 ==0 Total index is controlled directly by xndx. ie. the indexing starts from the start of the table. !=0 Start indexing from somewhere else in the table. Value must be positive and less than the table length (ixmode = 0) or less than 1 (ixmode !=0 iwgmode Default 0 ==0 Limit mode } See below ==1 Wrap mode } ==2 Guardpoint mode } 0 = Limit mode -------------- Limit the total index (ndx + ixoff) to between 0 and the guard point. For a table of length 5, this means that locations 0 to 3 and location 4 (the guard point) can be written. A negative total index writes to location 0. Total indexes > 4 write to location 4. 1 = Wrap mode ------------- Wrap total index value into locations 0 to E, where E is one less than either the table length or the factor of 2 number which is one less than the table length. For example, wrap into a 0 to 3 range - so that total index 6 writes to location 2. 2 = Guardpoint mode ------------------- The guardpoint is written at the same time as location 0 is written - with the same value. This facilitates writing to tables which are intended to be read with interpolation for producing smooth cyclic waveforms. In addition, before it is used, the total index is incremented by half the range between one location and the next, before being rounded down to the integer address of a table location. Normally (igwmode = 0 or 1) for a table of length 5 - which has locations 0 to 3 as the main table and location 4 as the guard point, a total index in the range of 0 to 0.999 will write to location 0. ("0.999" means just less than 1.0.) 1.0 to 1.999 will write to location 1 etc. A similar pattern holds for all total indexes 0 to 4.999 (igwmode = 0) or to 3.999 (igwmode = 1). igwmode = 0 enables locations 0 to 4 to be written - with the guardpoint (4) being written with a potentially different value from location 0. With a table of length 5 and the iwgmode = 2, then when the total index is in the range 0 to 0.499, it will write to locations 0 and 4. Range 0.5 to 1.499 will write to location 1 etc. 3.5 to 4.0 will _also_ write to locations 0 and 4. This way, the writing operation most closely approximates the results of interpolated reading. Guard point mode should only be used with tables that have a guardpoint. Guardpoint mode is accomplished by adding 0.5 to the total index, rounding to the next lowest integer, wrapping it modulo the factor of two which is one less than the table length, writing the the table (locations 0 to 3 in our example) and then writing to the guard point if index == 0. tablew has no output value. The last three parameters are optional and have default values of 0. Caution with k rate table numbers --------------------------------- The following notes also apply to the tablekt and tableikt ugens which can now have their table number changed at k rate. At k rate or a rate, if a table number of < 1 is given, or the table number points to a non-existent table, or to one which has a length of 0 (it is to be loaded from a file later) then an error will result and the instrument will be deactivated. >>> 2 - tablegpw, tableleng, tablemix and tablecopy =================================================== tableleng --------- ir itableng ifn kr tableng kfn ifn i rate number of function table kfn k rate number of function table These return the length of the specified table. This will be a power of two number in most circumstances - it will not show whether a table has a guardpoint or not - it seems this information is not available in the table's data structure. If table is not found, then 0 will be returned. Likely to be useful for setting up code for table manipulation operations, such as tablemix and tablecopy. tablgpw ------- itablegpw ifn tablegpw kfn For writing the table's guard point, with the value which is in location 0. Does nothing if table does not exist. Likely to be useful after manipulating a table with tablemix or tablecopy. tablemix -------- tablemix kdft, kdoff, klen, ks1ft, ks1off, ks1g, ks2ft, ks2off, ks2g itablemix idft, idoff, ilen, is1ft, is1off, is1g, is2ft, is2off, is2g This ugen mixes from two tables, with separate gains into the destination table. Writing is done for klen locations, usually stepping forward through the table - if klen is positive. If it is negative, then the writing and reading order is backwards - towards lower indexes in the tables. This bidirectional option makes it easy to shift the contents of a table sideways by reading from it and writing back to it with a different offset. If klen is 0, no writing occurs. Note that the internal integer value of klen is derived from the ANSI C floor() function - which returns the next most negative integer. Hence a fractional negative klen value of -2.3 would create an internal length of 3, and cause the copying to start from the offset locations and proceed for two locations to the left. The total index for table reading and writing is calculated from the starting offset for each table, plus the index value, which starts at 0 and then increments (or decrements) by 1 as mixing proceeds. These total indexes can potentially be very large, since there is no restriction on the offset or the klen. However each total index for each table is ANDed with a length mask (such as 0000 0111 for a table of length 8) to form a final index which is actually used for reading or writing. So no reading or writing can occur outside the tables. This is the same as "wrap" mode in table read and write. These ugens do not read or write the guardpoint. If a table has been rewritten with one of these, then if it has a guardpoint which is supposed to contain the same value as the location 0, then call tablegpw afterwards. The indexes and offsets are all in table steps - they are not normalised to 0 - 1. So for a table of length 256, klen should be set to 256 if all the table was to be read or written. The tables do not need to be the same length - wrapping occurs individually for each table. kdft Destination function table. kdoff Offset to start writing from. Can be negative. klen Number of write operations to perform. Negative means work backwards. ks1ft ks2ft Source function tables. These can be the same as the destination table, if care is exercised about direction of copying data. ks1off ks2off Offsets to start reading from in source tables. ks1g ks2g Gains to apply when reading from the source tables. The results are added and the sum is written to the destination table. tablecopy --------- tablecopy kdft, ksft itablecopy idft, isft Simple, fast table copy ugens. Takes the table length from the destination table, and reads from the start of the source table. For speed reasons, does not check the source length - just copies regardless - in "wrap" mode. This may read through the source table several times. A source table with length 1 will cause all values in the destination table to be written to its value. Table copy cannot read or write the guardpoint. To read it use table read, with ndx = the table length. Likewise use table write to write it. To write the guardpoint to the value in location 0, use tablegpw. This is primarily to change function tables quickly in a real-time situation. kdft Number of destination function table. ksft Number of source function table. >>> 3 - tablera and tablewa =========================== These ugens read and write tables in sequential locations to and from an a rate variable. Some thought is required before using them. They have at least two major, and quite different, applications which are discussed below. ar tablera kfn, kstart, koff kstart tablewa kfn, asig, koff ar a rate distination for reading ksmps values from a table. kfn i or k rate number of the table to read or write. kstart Where in table to read or write. asig a rate signal to read from when writing to the table. koff i or k rate offset into table. Range unlimited - see explanation at end of this section. In one application, these are intended to be used in pairs, or with several tablera ugens before a tablewa - all sharing the same kstart variable. These read from and write to sequential locations in a table at audio rates, with ksmps floats being written and read each cycle. tablera starts reading from location kstart. tablewa starts writing to location kstart, and then writes to kstart with the number of the location one more than the one it last wrote. (Note that for tablewa, kstart is both an input and output variable.) If the writing index reaches the end of the table, then no further writing occurs and zero is written to kstart. For instance, if the table's length was 16 (locations 0 to 15), and ksmps was 5. Then the following steps would occur with repetitive runs of the tablewa ugen, assuming that kstart started at 0. Run no. Initial Final locations written kstart kstart 1 0 5 0 1 2 3 4 2 5 10 5 6 7 8 9 3 10 15 10 11 12 13 14 4 15 0 15 This is to facilitate processing table data using standard a rate orchestra code between the tablera and tablewa ugens: ;-------------------------------- ; kstart = 0 ; ; Read 5 values from table into an ; a rate variable. lab1: atemp tablera ktabsource, kstart, 0 ; Process the values using a rate code. atemp = log(atemp) ; ; Write it back to the table kstart tablewa ktabdest, atemp, 0 ; Loop until all table locations have ; been processed. if ktemp > 0 goto lab1 ; ; ;-------------------------------- The above example shows a processing loop, which runs every k cycle, reading each location in the table ktabsource, and writing the log of those values into the same locations of table ktabdest. This enables whole tables, parts of tables (with offsets and different control loops) and data from several tables at once to be manipulated with a rate code and written back to another (or to the same) table. This is a bit of a fudge, but it is faster than doing it with k rate table read and write code. Another application is: ;-------------------------------- ; kzero = 0 ; kloop = 0 ; ; kzero tablewa 23, asignal, 0 ; ksmps a rate samples written into ; locations 0 to (ksmps -1) of table 23. ; lab1: ktemp table kloop, 23 ; Start a loop which runs ksmps times, ; in which each cycle processes one of [ Some code to manipulate ] ; table 23's values with k rate orchestra [ the value of ktemp. ] ; code. ; ; tablew ktemp, kloop, 23 ; Write the processed value to the table. ; kloop = kloop + 1 ; Increment the kloop, which is both the ; pointer into the table and the loop if kloop < ksmps goto lab1 ; counter. Keep looping until all values ; in the table have been processed. ; asignal tablera 23, 0, 0 ; Copy the table contents back to an a rate ; variable. ;-------------------------------- koff This is an offset which is added to the sum of kstart and the internal index variable which steps through the table. The result is then ANDed with the lengthmask (000 0111 for a table of length 8 - or 9 with guardpoint) and that final index is used to read or write to the table. koff can be any value. It is converted into a long using the ANSI floor() function so that -4.3 becomes -5. This is what we would want when using offsets which range above and below zero. Ideally this would be an optional variable, defaulting to 0, however with the existing Csount orchestra read code, such default parameters must be init time only. We want k rate here, so we cannot have a default. Notes on tablera and tablewa ---------------------------- These are a fudge, but they allow all Csounds k rate operators to be used (with caution) on a rate variables - something that would only be possible otherwise by ksmps = 1, downsamp and upsamp. Several cautions: 1 - The k rate code in the processing loop is really running at a rate, so time dependant functions like port and oscil work faster than normal - their code is expecting to be running at k rate. 2 - This system will produce undesirable results unless the ksmps fits within the table length. For instance a table of length 16 will accomodate 1 to 16 samples, so this example will work with ksmps = 1 to 16. Both these ugens generate an error and deactivate the instrument if a table with length < ksmps is selected. Likewise an error occurs if kstart is below 0 or greater than the highest entry in the table - if kstart >= table length. 3 - kstart is intended to contain integer values between 0 and (table length - 1). Fractional values above this should not affect operation but do not achieve anything useful. 4 - These ugens do not do interpolation and the kstart and koff parameters always have a range of 0 to (table length - 1) - not 0 to 1 as is available in other table read/write ugens. koff can be outside this range but it is wrapped around by the final AND operation. 5 - These ugens are permanently in wrap mode. When koff is 0, no wrapping needs to occur, since the kstart++ index will always be within the table's normal range. koff != 0 can lead to wrapping. 6 - The offset does not affect the number of read/write cycles performed, or the value written to kstart by tablewa. 7 - These ugens cannot read or write the guardpoint. Use tablegpw to write the guardpoint after manipulations have been done with tablewa. >>> 4 - The "zak" system for patching signals ============================================= "zak" means a or k rate patching, (i rate too), with a z at the start of the names of the ugens. This is a fudge to do the work until arrays are implemented. I want to use such facilities and will use zak for the time being. The zak system uses one area of memory as a global i or k rate patching area, and another for audio rate patching. These are establised by a ugen which must be called once only: zakinit isizea, isizek isizea The number of audio rate "locations" for a rate patching. Each "location" is actually an array which is ksmps long. isizek The number of locations we want to reserve for floats in the zk space. These can be written and read at i and k rates. eg. zakinit 10 30 reserves memory for locations 0 to 30 of zk space and for locations 0 to 10 of a rate za space. With ksmps = 8, this would take 31 floats for zk and 80 floats for za space. At least one location is always allocated for both za and zk spaces. There is nothing wrong with having za and zk ranges thousands or tens of thousands, but most pieces probably only need a few dozen to patch their signals around. These patching locations can be referred to by number with the following ugens. The easiest way to run zakinit just once is to put it outside any instrument definition. Typically this would be at the start of the orchestra file, with the sr etc. definitions. All code outside the instrument definitions is treated as instrument one and is given an init run at time = 0. zir, zkr, zkw ------------- There are two short, simple, fast opcodes which read a location in zk space, at either i time or at the k rate. ir zir indx kr zkr kndx Likewise, two write to a location in zk space at i time or at the k rate. ziw isig, indx zkw ksig, kndx These are fast and always check that the index is within the range of zk space. If it is out of range, an error is reported and 0 is returned, or no writing takes place. isig i rate } Value to write to the zk ksig i or k rate } location. indx i rate } Which zk location to write it to. kndx i or k rate } For instance, zkw kzoom, p8 can be used so that parameter 8 of the instrument's command line could control where in zk space the output is written. zkw kzoom, 7 This will always write it to zk location 7. kxxx phasor 1 kdest = 40 + kxxx * 16 zkw kzoom, kdest This will write kzoom to locations 40 to 55 on a one second scan cycle. zar, zaw -------- For a rate reading and writing, we use similar opcodes: ar zar kndx Reads number kndx array of floats which are the ksmps number of audio rate floats to be processed in a k cycle. zaw asig, kndx Writes into the array specified by kndx. In both cases, the ugen figures out where the array is and auto indexes through it to get each of the ksmps number of samples. The za space is separate from the zk space. These are the basic zk and za read and write ugens. However there are a number of luxuriant variants: ziwm, zkwm ---------- ziwm isig, indx [,imix] zkwm ksig, kndx [,imix] Like ziw and zkw above, except that they can mix - add the sig to the current value of the variable. If no imix is specified, they mix, but if imix is used, then 0 will cause writing (like ziw and zkw) any other value will cause mixing. zkmod ----- kr zkmod ksig, kzkmod zkmod is a unit generator intended to facilitate the modulation of one signal by another, where the modulating signal comes from a zk variable. Either additive or mulitiplicative modulation is provided. ksig is the input signal, to be modulated and sent to the output of the zkmod unit generator. kzkmod controls which zk variable is used for modulation. A positive value means additive modulation, a negative value means multiplicative modulation. A value of 0 means no change to ksig - it is transferred directly to the output. For instance kzkmod = 23 will read from zk variable 23, and add the value it finds there to ksig. If kzkmod = -402, then ksig is multiplied by the value read from zk location 402. kskmod can be an i or a k rate value. zkcl ---- zkcl kfirst, klast This will clear to zero one or more variables in the zk space. Useful for those variables which are accumulators for mixing things during the processing for each cycle, but which must be cleared to zero before the next set of calculations. zar, zarg, zaw, zawm -------------------- For a rate reading and writing, in the za space, we use similar opcodes: ar zar kndx kndx Points to which za variable to read. This reads the number kndx array of floats in za space which are the ksmps number of audio rate floats to be processed in a k cycle. ar zarg kndx, kgain Similar to zar, but multiplies the a rate signal by a k rate value kgain. zaw asig, kndx Writes asig into the za variable specified by kndx. zawm asig, kndx [,imix] Like zaw above, except that it can mix - add the asig to the current value of the destination za variable. If no imix is specified, it mixes, but if imix is used, then 0 will cause a simple write (like zaw) and any other value will cause mixing. zamod ----- zamod asig, kzamod Modulation of one audio rate signal by a second one - which comes from a za variable. The location of the modulating variable is controlled by the i or k rate variable kzamod. This is the audio rate version of zkmod described above. zacl ---- zacl kfirst, klast This will clear to zero one or more variables in the za space. Useful for those variables which are accumulators for mixing things during the processing for each cycle, but which must be cleared to zero before the next set of calculations. Summary of zak ugens -------------------- What types of input variables are used? Runs at time ir zir indx i kr zkr kndx k ziw isig, indx i zkw ksig, kndx k ziwm isig, indx, imix i zkwm ksig, kndx, kmix k zkcl kfirst, klast k ar zar kndx k but does arrays ar zarg kndx, kgain k but does arrays zaw asig, kndx k but does arrays zawm asig, kndx, kmix k but does arrays zacl kfirst, klast k but does arrays isig } indx } Known at init time imix } ksig } kndx } kmix } k rate variables kfirst } klast } kgain } asig } a rate variable - an array of floats. Known bugs in zak system ------------------------ When using the mix function of zkwm or zawm, care must be taken that the variables mixed to are zeroed at the end (or start) of each k cycle. The same applies to any variables to which signals are mixed. If you keep adding signals to them, their values can drift to astronomical figures - which is probably not what you want. My intention is to have certain ranges of za and zk variables used for mixing - I use zkcl and zacl in the last instrument to clear those ranges. >>> 5 - Six simple time reading ugens ===================================== timek, timek, times, itimes --------------------------- These read absolute time since the start of the performance - in two formats. One is timek or itimek for time in krate cycles. So with: sr = 44100 kr = 6300 ksmps = 7 then after half a second, the timek or itimek ugen would report 3150. It will always report an integer. Time in seconds is available with times or itimes. These would return 0.5 after half a second. kr timek kr times Both the above expect a k rate variable for output. There are no input parameters. For similar ugens which only operate at the start of the instance of the instrument: ir itimek ir itimes Both these expect an i rate variable (starting with i or gi) as their output. instimek, instimes ------------------ kr instimek kr instimes These are similar to timek and times, except they return the time since the start of this instance of the instrument. 6 - Printing k rate variables on the screen as numbers ====================================================== I hate debugging - these ugens are intended to facilitate the debugging of orchestra code. printk ------ printk prints one k rate value on every k cycle, every second or at intervals specified. First the instrument number is printed, then the absolute time in seconds, then a specified number of spaces, then the value. The variable number of spaces enables different values to be spaced out across the screen - so they are easier to view. printk kval, ispace [, itime] kval The number to be printed. ispace How many spaces to insert before it is printed. (Max 130.) itime How much time in seconds is to elapse between printings. (Default 1 second.) The first print is on the first k cycle of the instance of the instrument. This may not be 0.000 seconds, but the first k cycle afterwards. I want to investigate this - I thought that k rate code should run from time 0. printks ------- printks is a completely different ugen - similar to printf() in C. It is highly flexible, and if used together with cursor positioning codes, could be used to write specific values to locations in the screen as the Csound processing proceeds. With MSDOS, a colour screen and ANSI.SYS, it would be possible to have multiple colours, flashing displays - looking like NASA mission control, with k rate variables controlling the values displayed, the location on the screen where they are displayed, their colour etc. There is also a special mode where a float variable is rounded to the next lowest integer, and ANDed with 0 1111 1111 to produce a character between 0 and 255 to be sent to be printed. This elaborate use is a bit over the top - a hacker's paradise. But printks can be used simply, just to print variables for debugging. printks prints numbers and text, with up to four printable numbers - which can be i or k rate values. printks "txtstring", itime, kval1, kval2, kval3, kval4 txtstring Text to be printed first - can be up to 130 characters at least. _Must_ be in double quotes. The string is printed as is, but standard printf %f etc. codes are interpreted to print the four parameters. However (at least with DJGPP) the \n style of character codes are not interpreted by printf. This ugen therefore provides certain specific codes which are expanded: \n or \N Newline \t or \T Tab ^ Escape character ^^ ^ ~ Escape and '[' These are the lead in codes for MSDOS ANSI.SYS screen control characters. ~~ ~ An init error is generated if the first parameter is not a string of length > 0 enclosed in double quotes. [For some reason (at least with the DJGPP version, the program crashes if a null string - "" - is given. This seems not to be due to this ugen. This should be tidied up sometime.] A special mode of operation allows this ugen to convert kval1 input parameter into a 0 to 255 value and to use it as the first character to be printed. This enables a Csound program to send arbitrary characters to the console - albeit with a little awkwardness. [printf() does not have a format specifier to read a float and turn it into a byte for direct output. We could add extra code to do this if we really wanted to put arbitrary characters out with ease.] To acheive this, make the first character of the string a # and then, if desired continue with normal text and format specifiers. Three more format specifers may be used - they access kval2, kval3 and kval4. itime How much time in seconds is to elapse between printings. (Default 1 second.) kvalx The k rate values to be printed. Use 0 for those which are not used. For instance: printks "Volume = %6.2f Freq = %8.3f\n", 0.1, kvol, kfreq, 0, 0 This would print: Volume = 1234.56 Freq = 12345.678 printks "#x\\y = %6.2\n", 0.1, kxy, 0, 0, 0 This would print a tab character followed by: x\y = 1234.56 Discussion ---------- Both these printing ugens can be made to run on every k cycle - or at least every k cycle they are run in the instrument. Conditional goto statements can be used to run them only at certain times or when something goes wrong. To make them run on every k cycle like this, set itime to 0. When itime is not 0, then (if the orchestra code runs the ugen on every k cycle) then the ugen will decide when to print. It will always print on the first k cycle it is called. This means that if you set one of these to print only every 10 seconds, and conditional code in the instrument causes it to be run for the very first time at 3 seconds, then it will print at 3 seconds. Subsequent runs of the ugen at between 3 and 9.999 seconds would not cause it to print. This could be very useful - set the time to longer than the piece and conditional code in the instrument can be used to report a bug just once, on its first occurrence. You almost certainly do not want a print operation happening every k cycle - it slows the program down too much. Staying with the 10 second cycle example, if such a printk or printks ugen was called every k cycle, then it would print at 0 seconds (actually the first k cycle after 0), at 10.0 seconds, at 20.0 seconds etc. The time cycles start from the time the ugen is initialized - typically the initialisation of the instrument. Damien Miller pointed out an interesting application of these ugens - get the output of the program and sort the lines with a line sorter. The result would be the printed lines sorted first by instrument number, and then by time - for printk. However printks can be made to produce almost anything. The instrument is available as p1 and the time can easily be found and made available as a printks parameter. One option I have considered but not implemented is for these printed lines to be written to a file as well as to the screen. Let me know if you like this idea, or have any other ideas about debugging. printf() style %f formatting ---------------------------- One of the less enjoyable parts of C programming is trying to figure out what magic incantations to offer to printf() All the parameters are floats, so this reduces our decisions to two main issues: 1 - How many decimal points of precision do we want? (0 means no decimal point.) 2 - How many digits (or spaces) do we want printed in total - _including_ those after the decimal point? %f Just prints with full precision - 123.123456 %6.2f Prints 1234.12 %5.0f Prints 12345 There is more to the printf() codes than this - see an ANSI C library reference. Instead of 'f', you can use 'e' to get scientific notation. Using any other format specifiers than f, e, g, E and G will cause unpredictable results, because the parameters are always floating point numbers. >>> 7 - Why arrays or "zak" are so important for some applications ================================================================== A major theme of my approach to making music is to set up processes and let them interact and be affected by random occurrences. This can be expensive in analog hardware - but a load of fun too. Setting up a garden of interacting processes and then tweaking them to whatever state of control or chaos I like is my idea of fun! Lets say I want to set up a musical cellular automata - with 100 similar cells. Each one produces sound and has various internal states stored as i, k or a rate variables. The behaviour of each cell is at least partially dependant on that of its neighbours. Typically, each cell would make some of its own internal state - including sound output - _readable_ by its neigbours or other things. There could be a global matron function who tries to control the cells' level of friskiness if they individually or collectively incur her wrath by becoming too obstreperous. So I have a 10 x 10 array of cells, and their internal state is made available as global variables - with different names for the same variable in different cells. This could be done with 100 carefully written instruments, but life is too short. The only alternative is to use one instrument and have each instance decide where its interal states are written to for others to read. It should decide which of the 99 other instances it will read the states of. The ideal way is if we could write global variables as: gahuey[p7] = afoo * abar or gahuey[kdest] = afoo * abar In either case, one element of an array huey[] of a rate variables is written. (Actually each variable is an array of ksmps floats.) [ Interlude 1 - from what are the popular C variables foo and bar [ derived? See the end of the file. Likewise we want to be able to write these array specifications in the right hand of equations. gaduey[kdest] = huey[ksource] * (ablah + p4) So that is the first thing about arrays - make them easy and direct to use with i or k rate indexing. Secondly, make them multidimensional: galouey[4, 10] Is a two dimensional array of global audio rate variables. gkblah[2, 4, 10] Is a three dimensional array of global k rate variables. Thirdly, we want them to be either global or local to the instance of the instrument. This is quite a tall order, since the core of Csound is not perfect and is largely devoid of comments. Such facilities are obviously beyond what Csound was originally conceived to do, but now that CPUs are so much faster, many people will be writing more sophisticated programs. Since PCs with dual Pentiums exist today, and in a year or two will be available with up to four P6 processors, lets think big! In principle, the global aspect of arrays can be acheived with the zak system, but it is trickier. zak ugens do not go on the left or right of equations, they have their own line. They must write to normal variables and be fed by normal variables. Arrays, and multi dimensional arrays can all be done with offsets and multiplications to arrive at the final number of the location in za or zk space - but it this involves bulky, hard do debug and understand .orc code, and there is no prospect for building mnemonic names into the way these variables are accessed. I intend to do some cellular automatata or use multiple reverb and sound source instruments with varying delay times between them, all mixed with my binaural model - with the instruments, reverb points (and hence their connecting time delays) potentially moving around. There are great prospects for many hours of programming work, bogging down the CPU, and probably horrible results - but I am intrigued. >>> 8 - MSDOS ANSI screen codes =============================== I will write these up later. - - - - o o o o 0 0 0 0 o o o o - - - - [ Answer to Interlude 1: [ [ foo and bar were apparently derived from US Army WWII terminology [ in which FUBAR stood for "Fucked Up Beyond All Recognition". [ [ This was a linguistically fruitful time, giving us OK - "Orl Korrect" [ and SPAM - "Shit Parading As Meat". [