From CSWiki
Revision as of 08:46, 17 March 2008 by Kassen (talk | contribs) (Asynchrony)

Jump to: navigation, search

These Wiki pages are for coordinating development of the ChucK I/O API specification. The ChucK I/O API will consist of a main IO class with methods universal to all types of I/O, and three subclasses: FileIO, NetIO, and PipeIO.

Your comments, suggestions, and criticisms are much appreciated. The quality of the I/O API specification should primarily be evaluated on the following three criteria:

  1. Completeness: you can accomplish any I/O task you might want to perform in ChucK (or at least most of them).
  2. Elegance: ChucK I/O code is intuitive and easy to write.
  3. Feasibility: it is not too difficult to actually implement all of the components of this API without interfering with existing ChucK code and conventions.

The following is subject to change.

ChucK I/O Conventions

Class Hierarchy


I/O operations in ChucK are asynchronous by default. Calling an I/O function places the desired I/O operation in a queue, so that I/O does not interfere with real-time audio. This means that after some I/O function has been executed, you cannot be sure at any subsequent line of code that the operation has actually finished unless you do one of the following:

  1. Call the finish function, which waits for all queued I/O operations to finish without advancing time.
  2. Advance time until the I/O queue is cleared, by writing something like myIO => now;.
Would it make sense to use the same syntax here that's also used for the HID cue? With HID you use while( hi.recv( msg ) ){//parsing goes here} to make sure all messages in the cue get processed before we go on to the next event. I could imagine dealing with a out-going cue using something like while( hi.send( msg ) ){ms => now;}. Actually this would make sense for MIDI as well. There is currently nothing preventing you from writing ChucK code that attempts to send MIDI much faster then the protocol could possibly handle... which could lead to issues. I realize all of these are equivalent in functionality, I'm commenting in the interest of consistency. I could also imagine some of your ideas could be applied to HID/MIDI/etc as well.(Kas.)

ASCII vs. Binary

IO objects can operate in either ASCII or binary mode.

  • In ASCII mode, files are interpreted and written as plain text. This means, for example, that calling write(12345) will write the string "12345" to the file, and calling readInt() will cause the next whitespace-delimited word in the file to be parsed as an integer and returned.
  • In binary mode, files are interpreted as binary data in machine endian order. This means, for example, that calling write(12345) will write four bytes to the file containing the integer 12345, and calling readInt() will cause the next four bytes in the file to be returned as a signed integer.

IO Class

This class cannot be instantiated. In ChucK code, its main purpose is to enable functions to accept and read from any kind of I/O class without regard to the type of I/O that is taking place. This class deliberately includes no "open" methods to start I/O, since this differs greatly depending on whether you are opening a file, a network socket, or a pipe.


fun void close() 
Closes the I/O stream in the appropriate way (close file, close socket, etc.). Automatically calls finish first, to ensure all I/O operations are completed before closing. If you want to allow time to advance while the final I/O operations are being performed, you must write your_IO_object => now; before calling close.
fun int isClosed() 
Returns true if the IO object is closed (not ready for reading or writing), false otherwise.
fun void finish() 
Wait for queued I/O operations to finish without advancing time.
fun int isFinished() 
Returns true if there are no queued I/O operations, false otherwise.


fun string read(int length) 
Reads up to the specified number of bytes into a string.
fun string readLine() 
Reads everything up to and including the next newline character into a string.
fun int readInt([int flags]) 
Reads in one integer. The flags can optionally be used to specify the bit length of the integer to be read in when in binary mode (default is IO.READ_INT32). In ASCII mode, characters are read until the next whitespace character and the result is parsed to an integer. The value returned will always be a 32-bit signed ChucK integer, regardless of the number of bits read from the file.
fun int readFloat() 
Reads in one float. In ASCII mode, characters are read until the next whitespace character and the result is parsed to a float.
fun int eof() 
Returns true if end of file has been reached (or the network connection has been closed), or false otherwise.


fun void write(string s) 
Writes out the specified string.
fun void writef(string s, ...) 
Writes out the specified string, generated using printf-style conversion specifications.
fun void write(int i) 
Writes out the specified int.
fun void write(float f) 
Writes out the specified float.


Chucking an IO object to now (for example, as in myIO => now;) causes time to be advanced until all queued I/O operations for that object have completed.


The stream-ChucK operators can also be used to read from and write to a file in "Pac-Man" fashion.

For example, to write a series of variables to an IO object, you might write:


You could also write this in shorthand using the stream-ChucK operators, as:

myIO <~ myInt1 <~ myInt2 <~ myFloat <~ myStr;

To read a series of variables from an IO object, you might write:

myIO.readFloat() => myFloat;
myIO.readInt() => myInt1;
myIO.readLine() => myStr;
myIO.readInt() => myInt2;

You could also write this in shorthand using the stream-ChucK operators, as:

myIO ~> myFloat ~> myInt1 ~> myStr ~> myInt2;


These flags can optionally be passed to the readInt function when in binary mode to specify the bit length of the integer to be read. They are ignored in ASCII mode.


fun void ioexample(IO myIO) {

    // Read some integers
    int intarray[10];
    for (0 => int i; i < intarray.size(); i++)
        myIO ~> intarray[i]; // same as myIO.readInt() => intarray[i]
    // We cannot be sure that intarray is actually filled up until
    // we wait for the I/O queue to clear
    myIO => now; // allow time to advance while I/O is processing
    // Add them up
    int sum;
    for (0 => int i; i < intarray.size(); i++)
        intarray[i] +=> sum;
    // Write the result back out
    myIO <~ sum;
    // All finished
    myIO => now; // Allow I/O to finish while time advances before we close
    myIO.close(); // Automatically calls finish, but it will have no effect
                  // here because we already waited for I/O to finish while
                  // allowing time to advance.