Tutorial 18: Data Collections
Introduction
This tutorial covers two of the most useful objects in the Max world: coll and route. The coll object allows for the indexed collection of data; you can think of it as a small and fast database for message data. The route object is also tied to the concept of indexing – it will send data out of a specific outlet based on the index (first entry) of a list.
Indexed data is very useful for complex routing of information. Often data is already indexed for you: MIDI controllers normally index the value with the controller number, while note information is often indexed by the MIDI channel. Using indexing for data can also help when sending messages throughout a complex patch – by creating an indexing system, you can send data around indiscriminately, and have the route object find and decode the data as necessary.
To open the tutorial patch, click on the Open Tutorial button in the upper right-hand corner of the documentation window.
Basic coll handling
There are several small- to medium-sized patches scattered throughout the interface for this tutorial. We will be starting with the patch at the top-left side.
The first patch (labeled 1) introduces the coll object. Three message boxes are connected to it. If you click all three, you will load the coll object with three value sets. The easiest way to see what is stored in the coll is to double-click on it; it will open an editing window that will show you that the first number was stored as an index, and the second number was stored as a value. To programmatically retrieve a value, you send an index value into the inlet – the output will be the value (or values) stored at that index location. If you use the number box to send a 0
to the coll object, it will output 20
(the value at index location 0); 1
and 2
will output -50
and 33
respectively. This is the most basic, and most common, use of the coll object – as a storage mechanism for indexed value sets.
The patch labeled 2 is slightly different in that it uses symbols, rather than numbers, as the indices. Click on the three store
message boxes, and again double-click on the coll to see the stored data. The data is similar to the first coll, but the indices are symbols (words) rather than numbers. Now, if you click on the message boxes to the left (foo
, bar
, biz
), the coll object will output the correct value for each of the symbols.
The third patch (labeled 3) has already-stored data in it. If you select any number between 0
and 3
, the coll will output the data for that index. In this case, rather than a single number, the coll is storing a string of symbols (in this case, the opening soliloquy of Shakespeare's Richard III); all of the symbols are output as a list. Prepending a set
message to the list allows us to display the value string in a message box.
In order to store the data within the patcher (as we’ve done with this example), you have to set an attribute in the coll object's Inspector. Unlock the patcher, and open the Inspector for the coll in patch 3. You will notice that the attribute labeled “Save Data With Patcher” is selected. When this attribute is set, any information stored in the coll will be written to the patcher file when it is saved, and is immediately accessible when the patch is opened again.
Using coll for drawing and playing
The center section of our patch (section 4) uses a coll to control both a drawing and a MIDI playback function. In this case, the coll is loaded with some pre-defined data; however, instead of having been stored in the patcher, this data is loaded from an external file containing data from an EEG readout. The filename is “eeg.txt”, and that filename is used as the argument for the coll, so the data is read into the coll when it is first initialized.
At the top of the patch is a simple little self-incrementing counter system. Each time a number is generated, it increments itself by an amount determined by the upper-right number box. By default, the increment is one, since that is what the + object contains. Playing through the data set at this speed, however, can take quite a while, as our data file contains many thousands of lines. If you change the number box to 15
(meaning that you increment the steps by 15
at each bang
generated by the metro ), you will see the EEG pattern much more clearly. The output of this number generator is used to retrieve the indexed data from the coll, and is sent to both the drawit subpatcher and a simple MIDI player (makenote / noteout). The MIDI player just uses the number (scaled to the 0-127
range of MIDI) as the MIDI note, while the drawit subpatcher uses the incoming value as the vertical offset of a scrolling drawn circle. Start the metro (with the toggle) and notice how different increment values for the counting change the way the EEG data unfolds. If you need to restart the file at the beginning, the message box labeled 0
will reset the int box to 0
again.
Storing data into a coll for later use is the best way to deal with large datasets. This EEG capture, which represents over 18,000 data points, would cause your patcher to be very large if it was stored in the patcher's file. Instead, the data can be captured into a coll, saved in a text file (using the write
message), and read into the coll at runtime (either with the file name as an argument, or using the read
message).
The next patch segment, labeled 5, shows a few more tricks that coll has up its sleeve. This coll has a name – cues
– that manages 8 different cues handled by the rest of the patch (double-click the coll object to look at what's stored inside). You can use the connected number box to fire off the messages, but you can also use the three message boxes to walk through the cues. The start
message moves you to the beginning of the coll contents, while the next
and previous
messages move forward and backward through the cues. This allows you to step through the coll in sequence, even if the entries are not numbered sequentially, and will automatically loop back to the start when the end is reached. This is the easiest way to implement a sequential step-through of a coll ’s contents. Without worrying about the route object just yet, step through the sequence using the start
, next
and previous
messages and observe how different actions are triggered by the output of the coll object.
There is also a small patch just to the right, where another coll is also named “cues”. If you double-click on this coll, you will see that it has the same contents. In fact, because it is named the same as the previously viewed coll, it is actually sharing the contents of the coll – much like the value object shared data through a naming convention. Using named colls that share their information allows you to have easy access to a dataset in different places in your patch, or even within subpatchers.
Message routing with route
The small patch in the lower-right shows the functionality of the route object: as you click on the message boxes, the messages are matched based on their identifier (the first item in their list); if the identifier matches one of the arguments to the route object, the message is stripped of its identifier and the remaining data (in this case, an integer) is sent out the outlet. The route object has one outlet for each argument, as well as a final outlet for items that don't match. This patch shows what happens if an unrecognized message is received: the zeppo
message is not enumerated in the route object, and therefore cannot be routed. Rather than ignoring the message, it sends the entire message out the right outlet – possibly to be used by another route object, or perhaps to be sent (as we have in this case) to the Max Console with the print object.
Patch segment 5 also uses the route object to take messages from the coll and parse them to perform actions on different objects in the patch. As we saw inside the coll, the data contains a symbol (m1
, m2
, m3
and pick
) and a data item (0/1
for m1-m3
, and bang
for the pick
symbol). When this message is received by the route object, it uses the first element (the symbol) to determine the output routing, and sends the rest of the message through that outlet. Hence, a message that begins with m1
will be routed through the first outlet, while a message that begins with pick
will be routed through the fourth.
If we use the start and next messages to walk through the cues coll, we will see that a sequence of actions is performed based on the first segment of the message. The metro objects are started and stopped, and the random object is triggered twice. You can see that the route object will send tagged message contents where they will be useful.
Summary
The coll object is a powerful way to work with indexed messages and data; the object provides methods to get specific messages or to step sequentially through the contents. You can share the contents between named coll objects – letting you have access to this data throughout your patch; coll can also read its data from external files. We also saw how the route object allows you to track and move messages based on the contents of the message, helping you to route the data that you need to different objects based on an index.