f03. FTM Basics — Vectors


1-Dimensional Matrix


A matrix is a multi-dimensional array of data. Via the ftm.object, FTM provides the fmat class for managing two-dimensional (2D) arrays of floating point numbers, stored in rows and columns. It's also easy to use fmat to describe a 1D array with the syntax fmat 128 1, which indicates an array with only one column, such as the fmat named array128 in the previous tutorial.


In situations having to do with audio, 1D arrays are commonly used for storing samples of audio, or waveforms, or envelopes and windows for shaping and processing audio, or control functions of arbitrary shape. In all of those situations, we're really dealing with a time-based function in which a one-dimensional (1D) array of numerical data describes a waveform or a shape over time. A matrix with only one column or one row is also called a vector. Since this is the most common format used in audio, FTM provides a class called fvec—a vector of floats.


There are also many cases in which one does need a 2D matrix, of course. A stereo sound is one example, and is most easily stored in a matrix with two columns—column 0 for the left channel and column 1 for the right channel. (In most sound file formats the channels are interleaved, with alternating samples for the left and right channels, but those channels still need to be separated into two streams in order to view or play or process the data successfully.) Likewise, the complex numbers of an FFT analysis of a sound would most commonly be stored in two columns, column 0 for the real numbers and column 1 for the imaginary numbers, or column 0 for amplitudes and column 1 for phases. If we want to deal with the columns independently, we can isolate them—treat them as a separate vector—by referring to them as a fvec.


fvec


The fvec class works by making a reference to a column or row (or diagonal) of data within a fmat. It doesn't create new data, but it contains a reference to a particular vector of data within a matrix, thus providing a way to access that particular data. Thus a fvec (a reference to a chosen vector within a fmat) is for the most part treated just the same as a 1D fmat.


You can create a static fvec object in the ftm.object box. The fmat class also has methods called colref and rowref that dynamically return a reference to a particular vector within the matrix. In the tutorial patch you can see the use of both static and dynamically-created vectors.


The fmat waveforms object at the top of the patch is a 2D matrix with 512 rows and two columns.


• Double-click on the fmat waveforms object to see its contents. If this were audio data imported from a soundfile, the left channel of audio would be in column 0 and the right channel would be in column 1. In this case, each column contains just one cycle of a waveform.


N.B. Why is there already data in this matrix before you've even done anything to put it there? The data has been saved as part of the patch (as can be done with the table and coll objects in Max). When programming, you can do this by checking the Persistence option in the object Inspector. The data that was in the fmat at the time the patch was saved will be there the next time the patch is opened. A fmat's persistence is indicated by a small dot on its left edge (and its dimension arguments disappear).


The two fvec objects just below that create two named vectors within $waveforms; the first one makes a reference to column 0 of $waveforms and the second makes reference to column 1. That means that $sine will now be a synonym for column 0 of $waveform, and $square will be a synonym for column 1 of $waveforms.


Vector Display


Once one starts dealing with large quantities of numbers, a graphic display is needed make sense of them. For vectors, this is usually best done as a graph of the values (y axis) over the indices (x axis). FTM has a two versatile objects for viewing vectors—ftm.vecdisplay f03-01.jpg and ftm.editor f03-02.jpg. We'll use ftm.vecdisplay for now.


When ftm.vecdisplay receives a vector in its left inlet, it graphs the values, and also graphs any vectors it has recently received in its other inlets. You can use the Inspector to assign, for each inlet, the range of the y axis, the color of the graph, and the drawing style (points, lines, filled from 0 on the y axis, or filled from the bottom of the graph). Since we're graphing waveforms in this example, we have set the Min-Max Values (the range of the y axis) of each inlet to go from -1 to 1.


• Click on the message $sine outlined in red. The contents of the vector referred to by sine (column 0 of the fmat named waveforms) are graphed in the ftm.vecdisplay. Clicking on the message above that shows that one can also use the colref method to the fmat to specify a reference to a particular column. Now click on the message $square outlined in blue. Nothing gets drawn until you click again on the message $sine; ftm.vecdisplay waits for a vector in the left inlet before it draws all the recently received vectors.


(The waveform in column 1 doesn't look quite like a classic square wave; it's actually a "band-limited" square wave, consisting of only the first eight partials of a full square wave. The band-limited version is more visually distinctive than a true square wave, which would have only values of 1 and -1.)


The message (($sine + $square) * 0.5) demonstrates that one can use fvec references in expressions, just as one can use fmat references. The two vectors $sine and $square are added element-by-element, returning a new temporary vector that contains their sum. That vector is then multiplied by 0.5 (all values are halved), and the expression returns a vector containing the result (the average of $sine and $square).


• If you click on the button labeled "Draw all three", the three different vectors will be sent into the inlets, with the left inlet being triggered last, thus drawing all three vectors.


A 1D matrix can be displayed just like a fvec because it's already a vector. However, a two-column fmat will not be handled well by ftm.vecdisplay. To show a two-column matrix, use the colref method to refer to each column independently, and send them into two different inlets of ftm.vecdisplay.


• Click on the red message $waveforms and you'll see that the 2D fmat is not displayed successfully. Now click on the green message to send column 1 to the second inlet and column 0 the left inlet of ftm.vecdisplay.


The ftm.vecdisplay object actually has many more capabilities, including showing a history of the vectors it has received in a given inlet as well as allowing the user to scrub through a vector like a lookup table; however, we'll leave those capabilities for future tutorials.


Dynamic Creation of Vectors


You've seen the dynamic creation of a fvec using fmat's colref method, With additional arguments to colref you can specify not only which column you want to access, but what row you want to start with and how long you want the vector to be. These attributes of the fvec are called its index (which column), its onset (where in the column to start), and its size (how many elements long it is). 


• The message ($waveforms colref 1 256 128) returns a reference to column 1 of $waveforms, starting with row 256 of the column (halfway in) and continuing for 128 values (1/4 of the column's length). Click on the message to display the third quarter of column 1 of $waveforms.


All that has been said and demonstrated here for colref applies equally for its counterpart rowref.


You can change the attributes of an existing vector using the methods index, onset, or size with a number argument. For example, if a vector has a size of 512 and you want to shorten it to 256, you can just send it the message size 256.


• In the next example message, we show how to view just the first half of the vector without changing the existing vector reference. To do that, you can just dynamically create a new temporary fvec, set it the same as the existing one, then shorten it with the size method. That's what is taking place in the message (((new fvec) set $sine) size 256). Click on the message to display the first 256 elements of the $sine vector. Now click on the $sine message in the red box again, and you'll see that that fvec has remained unchanged; that's because we copied it to a new fvec and resized it rather than changing the size of $sine itself.


A similar idea is demonstrated in the next message. The objective is to change the phase of the square wave by 1/4 cycle (which will require "rotating" it by 1/4 the size of the vector) then multiply it by the sine wave. Again, in order to do this without actually altering the contents of the fmat waveforms, we dynamically create a new fmat, set its contents to be the same as $square (thus effectively making a copy of $square), rotate that with fmat's rotate method, and then do a vector multiplication of the new rotated vector with the original $sine vector. The returned result is a fvec containing the product of the two waveforms.


Importing Audio


The import method of fmat loads in a file. The file can be either a text file containing numbers or a sound file. The fmat will resize itself to accommodate the size of the file. If it's a sound file, each channel of audio will be stored in a column of the fmat. To access a particular channel, use a fvec reference to a particular column.


• Click on the message import to load a sound file into the fmat. (Choose a really short one that makes sense to try to view in the tiny display area.) Then click on the button to send out a fmat reference. The ftm.mess ($1 colref 0) sends out a fvec reference to column 0 of that matrix, and it gets drawn in the ftm.vecdisplay.


• To get a description of all of the methods of fvec, click on the message (info class fvec). You'll note that fvec and fmat share many of the same methods; that's because a fvec is essentially just a 1D fmat. The differences in methods are primarily in cases where the method makes sense only for a 2D matrix or only for a 1D matrix.


Summary


Digital audio waveforms are one-dimensional arrays, also known as vectors. FTM provides a fvec class that is a reference to a vector—a specific 1D array—within a fmat. You can create a named fvec, which can then be called by its name, or you can create a fvec dynamically with the colref (or rowref) method of fmat. The ftm.vecdisplay object graphically displays a received fvec (and in fact can display up to five overlapping fvecs).


You can import a soundfile into fmat. The fmat will be resized to contain the sound file, with each channel of the soundfile being placed in a column of the fmat. To access a particular channel of that audio, you can refer to it with an fvec using the colref method of fmat.