FUNCTION TABLES
A function table is essentially the same as what other audio programming languages call a buffer, a table, a list or an array. It is a place where data can be stored in an ordered way. Each function table has a size: how much data (in Csound just numbers) can be stored in it. Each value in the table can be accessed by an index, counting from 0 to size-1. For instance, if you have a function table with a size of 10, and the numbers [1.1 2.2 3.3 5.5 8.8 13.13 21.21 34.34 55.55 89.89] in it, this is the relation of value and index:
VALUE | 1.1 | 2.2 | 3.3 | 5.5 | 8.8 | 13.13 | 21.21 | 34.34 | 55.55 | 89.89 |
INDEX | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
So, if you want to retrieve the value 13.13, you must point to the value stored under index 5.
The use of function tables is manifold. A function table can contain pitch values to which you may refer using the input of a MIDI keyboard. A function table can contain a model of a waveform which is read periodically by an oscillator. You can record live audio input in a function table, and then play it back. There are many more applications, all using the fast access (because function tables are stored in RAM) and flexible use of function tables.
How to Generate a Function Table
Each function table must be created before it can be used. Even if you want to write values later, you must first create an empty table, because you must initially reserve some space in memory for it.
Each creation of a function table in Csound is performed by one of the GEN Routines. Each GEN Routine generates a function table in a particular way: GEN01 transfers audio samples from a soundfile into a table, with GEN02 we can write values in "by hand" one by one, GEN10 calculates a waveform using information determining a sum of sinusoids, GEN20 generates window functions typically used for granular synthesis, and so on. There is a good overview in the Csound Manual of all existing GEN Routines. Here we will explain the general use and give simple examples for some frequent cases.
GEN02 And General Parameters For GEN Routines
Let's start with our example above and write the 10 numbers into a function table of the same size. For this, use of a GEN02 function table is required. A short description of GEN02 from the manual reads as follows:
f # time size 2 v1 v2 v3 ...
This is the traditional way of creating a function table by an "f statement" or an "f score event" (in relation for instance to "i score events" which call instrument instances). The input parameters after the "f" are the following:
- #: a number (as positive integer) for this function table;
- time: at which time the function table is made available (usually 0 = from the beginning);
- size: the size of the function table. This is a bit tricky, because in the early days of Csound just power-of-two sizes for function tables were possible (2, 4, 8, 16, ...). Nowadays nearly every GEN Routine accepts other sizes, but these non-power-of-two sizes must be declared as a negative number!
- 2: the number of the GEN Routine which is used to generate the table. And here is another important point which must be regarded. By default, Csound normalizes the table values. This means that the maximum is scaled to +1 if positive, and to -1 if negative. To prevent Csound from normalizing, a negative number must be given as GEN number (here -2 instead of 2).
- v1 v2 v3 ...: the values which are written into the function table.
So this is the way to put the values [1.1 2.2 3.3 5.5 8.8 13.13 21.21 34.34 55.55 89.89] in a function table with the number 1:
EXAMPLE 03D01_Table_norm_notNorm.csd
<CsoundSynthesizer> <CsInstruments> ;Example by Joachim Heintz instr 1 ;prints the values of table 1 or 2 prints "%nFunction Table %d:%n", p4 indx init 0 loop: ival table indx, p4 prints "Index %d = %f%n", indx, ival loop_lt indx, 1, 10, loop endin </CsInstruments> <CsScore> f 1 0 -10 -2 1.1 2.2 3.3 5.5 8.8 13.13 21.21 34.34 55.55 89.89; not normalized f 2 0 -10 2 1.1 2.2 3.3 5.5 8.8 13.13 21.21 34.34 55.55 89.89; normalized i 1 0 0 1; prints function table 1 i 1 0 0 2; prints function table 2 </CsScore> </CsoundSynthesizer>
Instrument 1 just serves to print the values of the table (the tablei opcode will be explained later). See the difference whether the table is normalized (positive GEN number) or not normalized (negative GEN number).
Using the ftgen opcode is a more modern way of creating a function table, which is usually preferable to the old way of writing an f-statement in the score.1 The syntax is explained below:
giVar ftgen ifn, itime, isize, igen, iarg1 [, iarg2 [, ...]]
- giVar: a variable name. Each function is stored in an i-variable. Usually you want to have access to it from every instrument, so a gi-variable (global initialization variable) is given.
- ifn: a number for the function table. If you type in 0, you give Csound the job to choose a number, which is mostly preferable.
The other parameters (size, GEN number, individual arguments) are the same as in the f-statement in the score. As this GEN call is now a part of the orchestra, each argument is separated from the next by a comma (not by a space or tab like in the score).
So this is the same example as above, but now with the function tables being generated in the orchestra header:
EXAMPLE 03D02_Table_ftgen.csd
<CsoundSynthesizer> <CsInstruments> ;Example by Joachim Heintz giFt1 ftgen 1, 0, -10, -2, 1.1, 2.2, 3.3, 5.5, 8.8, 13.13, 21.21, 34.34, 55.55, 89.89 giFt2 ftgen 2, 0, -10, 2, 1.1, 2.2, 3.3, 5.5, 8.8, 13.13, 21.21, 34.34, 55.55, 89.89 instr 1; prints the values of table 1 or 2 prints "%nFunction Table %d:%n", p4 indx init 0 loop: ival table indx, p4 prints "Index %d = %f%n", indx, ival loop_lt indx, 1, 10, loop endin </CsInstruments> <CsScore> i 1 0 0 1; prints function table 1 i 1 0 0 2; prints function table 2 </CsScore> </CsoundSynthesizer>
GEN01: Importing a Soundfile
GEN01 is used for importing soundfiles stored on disk into the computer's RAM, ready for for use by a number of Csound's opcodes in the orchestra. A typical ftgen statement for this import might be the following:
varname ifn itime isize igen Sfilnam iskip iformat ichn giFile ftgen 0, 0, 0, 1, "myfile.wav", 0, 0, 0
- varname, ifn, itime: These arguments have the same meaning as explained above in reference to GEN02.
- isize: Usually you won't know the length of your soundfile in samples, and want to have a table length which includes exactly all the samples. This is done by setting isize=0. (Note that some opcodes may need a power-of-two table. In this case you can not use this option, but must calculate the next larger power-of-two value as size for the function table.)
- igen: As explained in the previous subchapter, this is always the place for indicating the number of the GEN Routine which must be used. As always, a positive number means normalizing, which is usually convenient for audio samples.
- Sfilnam: The name of the soundfile in double quotes. Similar to other audio programming languages, Csound recognizes just the name if your .csd and the soundfile are in the same folder. Otherwise, give the full path. (You can also include the folder via the "SSDIR" variable, or add the folder via the "--env:NAME+=VALUE" option.)
- iskip: The time in seconds you want to skip at the beginning of the soundfile. 0 means reading from the beginning of the file.
- iformat: Usually 0, which means: read the sample format from the soundfile header.
- ichn: 1 = read the first channel of the soundfile into the table, 2 = read the second channel, etc. 0 means that all channels are read.
The next example plays a short sample. You can download it here. Copy the text below, save it to the same location as the "fox.wav" soundfile (or add the folder via the "--env:NAME+=VALUE" option),2 and it should work. Reading the function table is done here with the poscil3 opcode which can deal with non-power-of-two tables.
EXAMPLE 03D03_Sample_to_table.csd
<CsoundSynthesizer> <CsOptions> -odac </CsOptions> <CsInstruments> ;Example by Joachim Heintz sr = 44100 ksmps = 32 nchnls = 2 0dbfs = 1 giSample ftgen 0, 0, 0, 1, "fox.wav", 0, 0, 1 instr 1 itablen = ftlen(giSample) ;length of the table idur = itablen / sr ;duration aSamp poscil3 .5, 1/idur, giSample outs aSamp, aSamp endin </CsInstruments> <CsScore> i 1 0 2.757 </CsScore> </CsoundSynthesizer>
GEN10: Creating a Waveform
The third example for generating a function table covers a classic case: building a function table which stores one cycle of a waveform. This waveform is then read by an oscillator to produce a sound.
There are many GEN Routines to achieve this. The simplest one is GEN10. It produces a waveform by adding sine waves which have the "harmonic" frequency relations 1 : 2 : 3 : 4 ... After the usual arguments for function table number, start, size and gen routine number, which are the first four arguments in ftgen for all GEN Routines, you must specify for GEN10 the relative strengths of the harmonics. So, if you just provide one argument, you will end up with a sine wave (1st harmonic). The next argument is the strength of the 2nd harmonic, then the 3rd, and so on. In this way, you can build the standard harmonic waveforms by sums of sinoids. This is done in the next example by instruments 1-5. Instrument 6 uses the sine wavetable twice: for generating both the sound and the envelope.
EXAMPLE 03D04_Standard_waveforms_with_GEN10.csd
<CsoundSynthesizer> <CsOptions> -odac </CsOptions> <CsInstruments> ;Example by Joachim Heintz sr = 44100 ksmps = 32 nchnls = 2 0dbfs = 1 giSine ftgen 0, 0, 2^10, 10, 1 giSaw ftgen 0, 0, 2^10, 10, 1, 1/2, 1/3, 1/4, 1/5, 1/6, 1/7, 1/8, 1/9 giSquare ftgen 0, 0, 2^10, 10, 1, 0, 1/3, 0, 1/5, 0, 1/7, 0, 1/9 giTri ftgen 0, 0, 2^10, 10, 1, 0, -1/9, 0, 1/25, 0, -1/49, 0, 1/81 giImp ftgen 0, 0, 2^10, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1 instr 1 ;plays the sine wavetable aSine poscil .2, 400, giSine aEnv linen aSine, .01, p3, .05 outs aEnv, aEnv endin instr 2 ;plays the saw wavetable aSaw poscil .2, 400, giSaw aEnv linen aSaw, .01, p3, .05 outs aEnv, aEnv endin instr 3 ;plays the square wavetable aSqu poscil .2, 400, giSquare aEnv linen aSqu, .01, p3, .05 outs aEnv, aEnv endin instr 4 ;plays the triangular wavetable aTri poscil .2, 400, giTri aEnv linen aTri, .01, p3, .05 outs aEnv, aEnv endin instr 5 ;plays the impulse wavetable aImp poscil .2, 400, giImp aEnv linen aImp, .01, p3, .05 outs aEnv, aEnv endin instr 6 ;plays a sine and uses the first half of its shape as envelope aEnv poscil .2, 1/6, giSine aSine poscil aEnv, 400, giSine outs aSine, aSine endin </CsInstruments> <CsScore> i 1 0 3 i 2 4 3 i 3 8 3 i 4 12 3 i 5 16 3 i 6 20 3 </CsScore> </CsoundSynthesizer>
How to Write Values to a Function Table
As we saw, each GEN Routine generates a function table, and by doing this, it writes values into it. But in certain cases you might first want to create an empty table, and then write the values into it later. This section is about how to do this.
Actually it is not correct to speak of an "empty table". If Csound creates an "empty" table, in fact it writes zeros to the indices which are not specified. This is perhaps the easiest method of creating an "empty" table for 100 values:
giEmpty ftgen 0, 0, -100, 2, 0
The basic opcode which writes values to existing function tables is tablew and its i-time descendant tableiw. Note that you may have problems with some features if your table is not a power-of-two size. In this case, you can also use tabw / tabw_i, but they don't have the offset- and the wraparound-feature. As usual, you must differentiate if your signal (variable) is i-rate, k-rate or a-rate. The usage is simple and differs just in the class of values you want to write to the table (i-, k- or a-variables):
tableiw isig, indx, ifn [, ixmode] [, ixoff] [, iwgmode] tablew ksig, kndx, ifn [, ixmode] [, ixoff] [, iwgmode] tablew asig, andx, ifn [, ixmode] [, ixoff] [, iwgmode]
- isig, ksig, asig is the value (variable) you want to write into specified locations of the table;
- indx, kndx, andx is the location (index) where you write the value;
- ifn is the function table you want to write in;
- ixmode gives the choice to write by raw indices (counting from 0 to size-1), or by a normalized writing mode in which the start and end of each table are always referred as 0 and 1 (not depending on the length of the table). The default is ixmode=0 which means the raw index mode. A value not equal to zero for ixmode changes to the normalized index mode.
- ixoff (default=0) gives an index offset. So, if indx=0 and ixoff=5, you will write at index 5.
- iwgmode tells what you want to do if your index is larger than the size of the table. If iwgmode=0 (default), any index larger than possible is written at the last possible index. If iwgmode=1, the indices are wrapped around. For instance, if your table size is 8, and your index is 10, in the wraparound mode the value will be written at index 2.
Here are some examples for i-, k- and a-rate values.
i-Rate Example
The following example calculates the first 12 values of a Fibonacci series and writes it to a table. This table has been created first in the header (filled with zeros). Then instrument 1 calculates the values in an i-time loop and writes them to the table with tableiw. Instrument 2 just serves to print the values.
EXAMPLE 03D05_Write_Fibo_to_table.csd
<CsoundSynthesizer> <CsInstruments> ;Example by Joachim Heintz giFt ftgen 0, 0, -12, -2, 0 instr 1; calculates first 12 fibonacci values and writes them to giFt istart = 1 inext = 2 indx = 0 loop: tableiw istart, indx, giFt ;writes istart to table istartold = istart ;keep previous value of istart istart = inext ;reset istart for next loop inext = istartold + inext ;reset inext for next loop loop_lt indx, 1, 12, loop endin instr 2; prints the values of the table prints "%nContent of Function Table:%n" indx init 0 loop: ival table indx, giFt prints "Index %d = %f%n", indx, ival loop_lt indx, 1, ftlen(giFt), loop endin </CsInstruments> <CsScore> i 1 0 0 i 2 0 0 </CsScore> </CsoundSynthesizer>
k-Rate Example
The next example writes a k-signal continuously into a table. This can be used to record any kind of user input, for instance by MIDI or widgets. It can also be used to record random movements of k-signals, like here:
EXAMPLE 03D06_Record_ksig_to_table.csd
<CsoundSynthesizer> <CsOptions> -odac </CsOptions> <CsInstruments> ;Example by Joachim Heintz sr = 44100 ksmps = 32 nchnls = 2 0dbfs = 1 giFt ftgen 0, 0, -5*kr, 2, 0; size for 5 seconds of recording giWave ftgen 0, 0, 2^10, 10, 1, .5, .3, .1; waveform for oscillator seed 0 ; - recording of a random frequency movement for 5 seconds, and playing it instr 1 kFreq randomi 400, 1000, 1 ;random frequency aSnd poscil .2, kFreq, giWave ;play it outs aSnd, aSnd ;;record the k-signal prints "RECORDING!%n" ;create a writing pointer in the table, ;moving in 5 seconds from index 0 to the end kindx linseg 0, 5, ftlen(giFt) ;write the k-signal tablew kFreq, kindx, giFt endin instr 2; read the values of the table and play it again ;;read the k-signal prints "PLAYING!%n" ;create a reading pointer in the table, ;moving in 5 seconds from index 0 to the end kindx linseg 0, 5, ftlen(giFt) ;read the k-signal kFreq table kindx, giFt aSnd oscil3 .2, kFreq, giWave; play it outs aSnd, aSnd endin </CsInstruments> <CsScore> i 1 0 5 i 2 6 5 </CsScore> </CsoundSynthesizer>
As you see, in this typical case of writing k-values to a table you need a moving signal for the index. This can be done using the line or linseg opcode like here, or by using a phasor. The phasor always moves from 0 to 1 in a certain frequency. So, if you want the phasor to move from 0 to 1 in 5 seconds, you must set the frequency to 1/5. By setting the ixmode argument of tablew to 1, you can use the phasor output directly as writing pointer. So this is an alternative version of instrument 1 taken from the previous example:
instr 1; recording of a random frequency movement for 5 seconds, and playing it kFreq randomi 400, 1000, 1; random frequency aSnd oscil3 .2, kFreq, giWave; play it outs aSnd, aSnd ;;record the k-signal with a phasor as index prints "RECORDING!%n" ;create a writing pointer in the table, ;moving in 5 seconds from index 0 to the end kindx phasor 1/5 ;write the k-signal tablew kFreq, kindx, giFt, 1 endin
a-Rate Example
Recording an audio signal is quite similar to recording a control signal. You just need an a-signal as input and also as index. The first example shows first the recording of a random audio signal. If you have live audio input, you can then record your input for 5 seconds.
EXAMPLE 03D07_Record_audio_to_table.csd
<CsoundSynthesizer> <CsOptions> -iadc -odac </CsOptions> <CsInstruments> ;Example by Joachim Heintz sr = 44100 ksmps = 32 nchnls = 2 0dbfs = 1 giFt ftgen 0, 0, -5*sr, 2, 0; size for 5 seconds of recording audio seed 0 instr 1 ;generating a band filtered noise for 5 seconds, and recording it aNois rand .2 kCfreq randomi 200, 2000, 3; random center frequency aFilt butbp aNois, kCfreq, kCfreq/10; filtered noise aBal balance aFilt, aNois, 1; balance amplitude outs aBal, aBal ;;record the audiosignal with a phasor as index prints "RECORDING FILTERED NOISE!%n" ;create a writing pointer in the table, ;moving in 5 seconds from index 0 to the end aindx phasor 1/5 ;write the k-signal tablew aBal, aindx, giFt, 1 endin instr 2 ;read the values of the table and play it prints "PLAYING FILTERED NOISE!%n" aindx phasor 1/5 aSnd table3 aindx, giFt, 1 outs aSnd, aSnd endin instr 3 ;record live input ktim timeinsts ; playing time of the instrument in seconds prints "PLEASE GIVE YOUR LIVE INPUT AFTER THE BEEP!%n" kBeepEnv linseg 0, 1, 0, .01, 1, .5, 1, .01, 0 aBeep oscils .2, 600, 0 outs aBeep*kBeepEnv, aBeep*kBeepEnv ;;record the audiosignal after 2 seconds if ktim > 2 then ain inch 1 printks "RECORDING LIVE INPUT!%n", 10 ;create a writing pointer in the table, ;moving in 5 seconds from index 0 to the end aindx phasor 1/5 ;write the k-signal tablew ain, aindx, giFt, 1 endif endin instr 4 ;read the values from the table and play it prints "PLAYING LIVE INPUT!%n" aindx phasor 1/5 aSnd table3 aindx, giFt, 1 outs aSnd, aSnd endin </CsInstruments> <CsScore> i 1 0 5 i 2 6 5 i 3 12 7 i 4 20 5 </CsScore> </CsoundSynthesizer>
How to Retreive Values from a Function Table
There are two methods of reading table values. You can either use the table / tab opcodes, which are universally usable, but need an index; or you can use an oscillator for reading a table at k-rate or a-rate.
The table Opcode
The table opcode is quite similar in syntax to the tableiw/tablew opcodes (which are explained above). It's just its counterpart in reading values from a function table instead of writing values to it. So its output is either an i-, k- or a-signal. The main input is an index of the appropriate rate (i-index for i-output, k-index for k-output, a-index for a-output). The other arguments are as explained above for tableiw/tablew:
ires table indx, ifn [, ixmode] [, ixoff] [, iwrap] kres table kndx, ifn [, ixmode] [, ixoff] [, iwrap] ares table andx, ifn [, ixmode] [, ixoff] [, iwrap]
As table reading often requires interpolation between the table values - for instance if you read k- or a-values faster or slower than they have been written in the table - Csound offers two descendants of table for interpolation: tablei interpolates linearly, whilst table3 performs cubic interpolation (which is generally preferable but is computationally slightly more expensive).3
Another variant is the tab_i / tab opcode which misses some features but may be preferable in some situations. If you have any problems in reading non-power-of-two tables, give them a try. They should also be faster than the table opcode, but you must take care: they include fewer built-in protection measures than table, tablei and table3 and if they are given index values that exceed the table size Csound will stop and report a performance error.
Examples of the use of the table opcodes can be found in the earlier examples in the How-To-Write-Values... section.
Oscillators
To read table values using an oscillator is standard when reading tables which contain one cycle of a waveform at audio-rate. But actually you can read any table using an oscillator, either at a- or at k-rate. The advantage is that you needn't create an index signal. You can simply specify the frequency of the oscillator.
You should bear in mind that many of the oscillators in Csound will work only with power-of-two table sizes. The poscil/poscil3 opcodes do not have this restriction and offer a high precision, because they work with floating point indices, so in general it is recommended to use them. Below is an example that demonstrates both reading a k-rate and an a-rate signal from a buffer with poscil3 (an oscillator with a cubic interpolation):
EXAMPLE 03D08_RecPlay_ak_signals.csd
<CsoundSynthesizer> <CsOptions> -iadc -odac </CsOptions> <CsInstruments> ;Example by Joachim Heintz sr = 44100 ksmps = 32 nchnls = 2 0dbfs = 1 ; -- size for 5 seconds of recording control data giControl ftgen 0, 0, -5*kr, 2, 0 ; -- size for 5 seconds of recording audio data giAudio ftgen 0, 0, -5*sr, 2, 0 giWave ftgen 0, 0, 2^10, 10, 1, .5, .3, .1; waveform for oscillator seed 0 ; -- ;recording of a random frequency movement for 5 seconds, and playing it instr 1 kFreq randomi 400, 1000, 1; random frequency aSnd poscil .2, kFreq, giWave; play it outs aSnd, aSnd ;;record the k-signal with a phasor as index prints "RECORDING RANDOM CONTROL SIGNAL!%n" ;create a writing pointer in the table, ;moving in 5 seconds from index 0 to the end kindx phasor 1/5 ;write the k-signal tablew kFreq, kindx, giControl, 1 endin instr 2; read the values of the table and play it with poscil prints "PLAYING CONTROL SIGNAL!%n" kFreq poscil 1, 1/5, giControl aSnd poscil .2, kFreq, giWave; play it outs aSnd, aSnd endin instr 3; record live input ktim timeinsts ; playing time of the instrument in seconds prints "PLEASE GIVE YOUR LIVE INPUT AFTER THE BEEP!%n" kBeepEnv linseg 0, 1, 0, .01, 1, .5, 1, .01, 0 aBeep oscils .2, 600, 0 outs aBeep*kBeepEnv, aBeep*kBeepEnv ;;record the audiosignal after 2 seconds if ktim > 2 then ain inch 1 printks "RECORDING LIVE INPUT!%n", 10 ;create a writing pointer in the table, ;moving in 5 seconds from index 0 to the end aindx phasor 1/5 ;write the k-signal tablew ain, aindx, giAudio, 1 endif endin instr 4; read the values from the table and play it with poscil prints "PLAYING LIVE INPUT!%n" aSnd poscil .5, 1/5, giAudio outs aSnd, aSnd endin </CsInstruments> <CsScore> i 1 0 5 i 2 6 5 i 3 12 7 i 4 20 5 </CsScore> </CsoundSynthesizer>
Saving the Contents of a Function Table to a File
A function table exists only as long as you run the Csound instance which has created it. If Csound terminates, all the data is lost. If you want to save the data for later use, you must write them to a file. There are several cases, depending firstly on whether you write at i-time or at k-time and secondly on what kind of file you want to write to.
Writing a File in Csound's ftsave Format at i-Time or k-Time
Any function table in Csound can easily be written to a file by the ftsave (i-time) or ftsavek (k-time) opcode. Their use is very simple. The first argument specifies the filename (in double quotes), the second argument chooses between a text format (non zero) or a binary format (zero) to write, then you just give the number of the function table(s) to save.
With the following example, you should end up with two textfiles in the same folder as your .csd: "i-time_save.txt" saves function table 1 (a sine wave) at i-time; "k-time_save.txt" saves function table 2 (a linear increment produced during the performance) at k-time.
EXAMPLE 03D09_ftsave.csd
<CsoundSynthesizer> <CsInstruments> ;Example by Joachim Heintz sr = 44100 ksmps = 32 nchnls = 2 0dbfs = 1 giWave ftgen 1, 0, 2^7, 10, 1; sine with 128 points giControl ftgen 2, 0, -kr, 2, 0; size for 1 second of recording control data seed 0 instr 1; saving giWave at i-time ftsave "i-time_save.txt", 1, 1 endin instr 2; recording of a line transition between 0 and 1 for one second kline linseg 0, 1, 1 tabw kline, kline, giControl, 1 endin instr 3; saving giWave at k-time ftsave "k-time_save.txt", 1, 2 endin </CsInstruments> <CsScore> i 1 0 0 i 2 0 1 i 3 1 .1 </CsScore> </CsoundSynthesizer>
The counterpart to ftsave/ftsavek are the ftload/ftloadk opcodes. Using them, you can load the saved files into function tables.
Writing a Soundfile from a Recorded Function Table
If you have recorded your live-input to a buffer, you may want to save your buffer as a soundfile. There is no opcode in Csound which does that, but it can be done by using a k-rate loop and the fout opcode. This is shown in the next example, in instrument 2. First instrument 1 records your live input. Then instrument 2 writes the "testwrite.wav" file into the same folder as your .csd. This is done at the first k-cycle of instrument 2, by repeatedly reading the table values and writing them as an audio signal to disk. After this is done, the instrument is turned off by executing the turnoff statement.
EXAMPLE 03D10_Table_to_soundfile.csd
<CsoundSynthesizer> <CsOptions> -i adc </CsOptions> <CsInstruments> ;Example by Joachim Heintz sr = 44100 ksmps = 32 nchnls = 2 0dbfs = 1 ; -- size for 5 seconds of recording audio data giAudio ftgen 0, 0, -5*sr, 2, 0 instr 1 ;record live input ktim timeinsts ; playing time of the instrument in seconds prints "PLEASE GIVE YOUR LIVE INPUT AFTER THE BEEP!%n" kBeepEnv linseg 0, 1, 0, .01, 1, .5, 1, .01, 0 aBeep oscils .2, 600, 0 outs aBeep*kBeepEnv, aBeep*kBeepEnv ;;record the audiosignal after 2 seconds if ktim > 2 then ain inch 1 printks "RECORDING LIVE INPUT!%n", 10 ;create a writing pointer in the table, ;moving in 5 seconds from index 0 to the end aindx phasor 1/5 ;write the k-signal tablew ain, aindx, giAudio, 1 endif endin instr 2; write the giAudio table to a soundfile Soutname = "testwrite.wav"; name of the output file iformat = 14; write as 16 bit wav file itablen = ftlen(giAudio); length of the table in samples kcnt init 0; set the counter to 0 at start loop: kcnt = kcnt+ksmps; next value (e.g. 10 if ksmps=10) andx interp kcnt-1; calculate audio index (e.g. from 0 to 9) asig tab andx, giAudio; read the table values as audio signal fout Soutname, iformat, asig; write asig to a file if kcnt <= itablen-ksmps kgoto loop; go back as long there is something to do turnoff ; terminate the instrument endin </CsInstruments> <CsScore> i 1 0 7 i 2 7 .1 </CsScore> </CsoundSynthesizer>
This code can also be transformed in a User Defined Opcode. It can be found here.
Related Opcodes
ftgen: Creates a function table in the orchestra using any GEN Routine.
table / tablei / table3: Read values from a function table at any rate, either by direct indexing (table), or by linear (tablei) or cubic (table3) interpolation. These opcodes provide many options and are safe because of boundary check, but you may have problems with non-power-of-two tables.
tab_i / tab: Read values from a function table at i-rate (tab_i), k-rate or a-rate (tab). Offer no interpolation and less options than the table opcodes, but they work also for non-power-of-two tables. They do not provide a boundary check, which makes them fast but also give the user the resposability not reading any value off the table boundaries.
tableiw / tablew: Write values to a function table at i-rate (tableiw), k-rate and a-rate (tablew). These opcodes provide many options and are safe because of boundary check, but you may have problems with non-power-of-two tables.
tabw_i / tabw: Write values to a function table at i-rate (tabw_i), k-rate or a-rate (tabw). Offer less options than the tableiw/tablew opcodes, but work also for non-power-of-two tables. They do not provide a boundary check, which makes them fast but also give the user the resposability not writing any value off the table boundaries.
poscil / poscil3: Precise oscillators for reading function tables at k- or a-rate, with linear (poscil) or cubic (poscil3) interpolation. They support also non-power-of-two tables, so it's usually recommended to use them instead of the older oscili/oscil3 opcodes. Poscil has also a-rate input for amplitude and frequency, while poscil3 has just k-rate input.
oscili / oscil3: The standard oscillators in Csound for reading function tables at k- or a-rate, with linear (oscili) or cubic (oscil3) interpolation. They support all rates for the amplitude and frequency input, but are restricted to power-of-two tables. Particularily for long tables and low frequencies they are not as precise as the poscil/poscil3 oscillators.
ftsave / ftsavek: Save a function table as a file, at i-time (ftsave) or k-time (ftsavek). This can be a text file or a binary file, but not a soundfile. If you want to save a soundfile, use the User Defined Opcode TableToSF.
ftload / ftloadk: Load a function table which has been written by ftsave/ftsavek.
line / linseg / phasor: Can be used to create index values which are needed to read/write k- or a-signals with the table/tablew or tab/tabw opcodes.
- Mainly because you can refer to the function table by a variable name and most not deal with tables numbers.^
- If youĆ .csd file is, for instance, in the directory /home/jh/csound, and your sound file in the directory /home/jh/samples, you should add this inside the <CsOptions> tag:
--env:SSDIR+=/home/jh/samples. This means: 'Look also in /home/jh/sample as Sound Sample Directory (SSDIR)'
^ - For a general introduction about interpolation, see for instance http://en.wikipedia.org/wiki/Interpolation^