TABREAD.TXT =========== [ http://www.firstpr.com.au/csound/tabread.txt ] [ ] [ Minor clarifications 27 December 1997 - such as updating the email ] [ address and fixing some things that were not accurate. ] Documentation on the table reading anomalies in the original Csound versions of ugens2.c/h and a description of how my new version of ugens2.c/h fixes these, to conform with my more detailed description of how table reading should behave. Also, how my new versions support the new opcodes: tablekt tableikt which enable table reading with the table number being changed at k rate. The new version is: ugens2.c 1 Feb 1996 bug fix of my 28 August 1995 version. ugens2.h 28 August 1995 version. Note that as of December 1997, this version of "ugens2" has not been incorporated into the "standard" Csound which is maintained by John Fitch. This means the standard Csound still has anomalous table read behaviour and cannot be used to read tables where the table number must be changed at k rate. To achieve this, my new ugens2 code needs to be included, with appropriate changes (as documented in the code) to entry.c, and with a new function "ftfindp()" added to fgens.c. A new fgens.c is at http://www.firstpr.com.au/csound/ but I have not checked if it is up-to- date with Csound versions of 1996/7. My fgens.c also extends the maximum table number to 1000. Installing my ugens2.c/h and the fgens.c/h will lead to logical, clearly defined (as per description below) table read behaviour, as well as the ability to change table number at k rate. The only disadvantage might be that any pieces which relied on what I consider to be the previous faulty or idiosyncratic behaviour of the table read ugens, would create different audio results. This seems like a small price to pay, because I figure that future Csound pieces are more important than old ones, and the old ones can always be run with older Csound executables. The following text is intended for Csound users and C programmers. It was originally written on or before 8 September 1995, but has been updated on 27 December 1997. Robin Whittle rw@firstpr.com.au http://www.firstpr.com.au/csound/ Contents -------- 1 - New feature in the table read code - k rate control of the table number. 2 - A clear definition of how table read should behave under all circumstances - to go with my (hopefully) bug free version of the code. 3 - A report on the bugs in table reading ugens which I fixed in July 95. 1 - Table number controllable at k rate ======================================= The ugens table and tablei, when producing a k or a rate result, can only use an init time variable to select the table number. The valid combinations of parameter types are shown below: ir table indx, ifn [, ixmode] [,ixoff] [,iwrap] ir tablei indx, ifn [, ixmode] [,ixoff] [,iwrap] kr table kndx, ifn [, ixmode] [,ixoff] [,iwrap] ar tablei andx, ifn [, ixmode] [,ixoff] [,iwrap] In my new version of ugens2.c/h, there is code for two new ugens which accept k rate control of the table number. In all other respects they are similar to my new versions of the "table" and "tableei" ugens. kr tablekt kndx, kfn [, ixmode] [,ixoff] [,iwrap] ar tableikt andx, kfn [, ixmode] [,ixoff] [,iwrap] In all these, the type of the input variables are indicated by the first letter of their name. 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 iwrap Default 0 ==0 Limit mode - when total index is below 0, then final index is 0. Total index above table length results in a final index of the table length - high out of range total indexes stick at the upper limit of the table. See discussion below for exact detailed differences between interpolated and non interpolated modes. !=0 Wrap mode - total index is wrapped modulo the table length so that all total indexes map into the table. For instance in a table of length 8, xndx = 5 and ixoff = 6 gives a total index of 11, which wraps to a final index of 3. See discussion below for finer points in interpolated mode. 2 - Detailed desciption of what the table read ugens should do ============================================================== This description will use an example table with 5 entries: Location 0 1 2 3 4 Value 10 11 12 13 10 The main body of the table is locations 0 to 3. Location 4 is the guard point. Not all tables have one, but if you are doing interpolated reading with total indexes above 3, then you will be reading both locations 3 and 4, and taking a fraction of each for your output. "Total index" means the index generated from the ndx and xoff parameters in the table or tablei command line, after being scaled by the "Normalisation factor". The "Normalisatin factor" is internal to the ugen. Usually it is 1 - so for this table, the range of the ndx and xoff would be 0 to 3 or 4. If parameter ixmode is set to 1, then the normalisation factor is set to the table length so that an input ranges of 0 to 1 can be used for both ndx and xoff. "Table length" is a tricky thing. In this discussion, I will use it as meaning the power of two number which is the table size, not including the guardpoint. There is something of a mystery about how this guardpoint survives, because the code which allocates memory seems to do it on the basis of the table length, whereas tables can be specified to have a guardpoint - one more than this length. The FUNC data structure which describes the table gives no indication of whether the table has a guard point. The table length there is always a power of 2. So in our example, the length is 4, but there are 5 entries including the guard point. Sorry about this terminology. The guardpoint seems intended to allow interpolated reading to produce a smooth waveform. In this case, its value is set to the same as that of location 0. However it could be set to any other value if the aim was not to produce a smooth waveform with an interpolated oscillator or cyclic table read. If location 4 contained 14, then an interpolated read would produce a sawtooth with a sharp edge at the end of the waveform - falling to 10 at index 0. So the basic function of ndx, ixmode and ixoff are now clear, as is the function of interpolated read. What does ixwrap do? Without ixwrap, or if it is set to 0, the ugen is in what I call "Limit mode". Its behavior differs depending on whether it is interpolated or not - but the concept is the same. Terminology: "Total index" is the sum of xndx and ixoff, after being scaled according to ixmode. "Final index" is the total index, after it has been limited to something within the length of the table, either as a result of "limit" or "wrap" mode. The final index determines where the table is read from and so determines the final result. In Limit mode, final indexes below 0 are limited to 0 and so the value of location 0 is read for all 0 and negative total indexes. This is the same for table and tablei. In non interpolated table read, total indexes higher than 3 are limited to a final index of 3, and the value at location 3 is read out. In interpolated table read, if the total index is above 4.0, it is limited to a final index of 4.0 and the value of the guardpoint is read out. This means that a set of 5 interpolated segments bridges the 0 to 4 total index range. Wrap mode - when ixwrap = 1 - is when the total index is truncated modulo the table length to give continuous scanning of the table no matter what the total index is. So in interpolate mode, a steadily increasing total index would produce a final index rising as a sawtooth from 0 to 3.9999 repeatedly. With interpolation this would produce a saw tooth wave rising from 10 to 13 in 3 segments, and then down to 10 in the fourth. Without interpolation, the output would have been a sequence of four square steps - 10, 11, 12 and 13 repeated. That is the total description of how I believe the table read ugens should behave. Some of this not spelt out in so much detail in the manual, but I believe that my description is compatible with the _intent_ of the manual. Please let me know if you disagree with any of this. 3 - Bugs ======== Short description of bugs ========================= There were bugs in the table and tablei ugens when the usual index was exceeded, or when with tablei, the offset did not point exactly to a location in the table. These have now been fixed in my new versions of ugens2.h and ugens2.c. These versions are fully commented, and as far as I am able to test them, bug free. Detailed bug report ------------------- The original code _may_ have varied its behaviour amongst compilers, due to the way negative floats are rounded to integers. This is machine dependant - so the actual behavior may have been different on different machines. What follows is a description of how the code behaved under DJGPP (DJ Delorie's free and wonderful GNU C++ compiler - see my separate doco on bringing it up) on a MSDOS 486 system. This is contrasted with the above description of how I think it should behave. Bug 1 Interpolated with out of range total indexes in limit mode ---------------------------------------------------------------- In the example given, if the total index increased from -4 to 0, the output should have remained at 0. Instead it traversed four little sawtooth waves - each a facsimile of the proper response from 0 to 1. The same trouble occurred for indexes above 4, when it should have stuck at the value of the guardpoint. Bug 2 High total indexes in limit mode, non interpolated -------------------------------------------------------- Total indexes beween 3.0 and 3.999 (3.999 means just less then 4.0) were working fine. What should have happened was that anything above 3 should have resulted in a final index of 3 - to read that value for all higher total indexes. Instead, when it was 4, or higher than 4, it was limited to 4 and then subjected to a binary AND with 3 - reducing the final index to 0! Bug 3 Interpolate read with offset not at an exact table location ----------------------------------------------------------------- Wierd things happened when xoff was not pointing exactly at a table location. xoff = 0 is fine. xoff = 3 or 2 is fine. xoff = 2.1 is not. 0.1 of each segment (or was it 0.9) would be interpolated from the next pair of values rather than this pair - a very messy squarewave error added to the sawtooth result we should normally get from this table. Bug 4 Negative total indexes in non interpolated, wrap mode ----------------------------------------------------------- Due to a problem rounding negative floats to integers (-2.3 was becoming 2 when we want it to go to the next most negative integer - - 3) the whole negative range of the output was shifted by one to the negative. Using the ANSI function floor() fixed it. Testing the compiled code ------------------------- I have developed TABLTEST.ORC and TABLTEST.SCO to test the table read ugenses. This is how I tested my compiled version - I cannot think of much more to do. This may be useful for testing your compiled version. Let me know if you want these files. They are not particularly well commented - you should look at the output on a dual trace CRO and figure out what all the 64 tests should look like - and spot the difference. The more formal definition of the behaviour of table read above will help. - Robin Whittle