tsnares v0.1
final project for MUMT 306 @ McGill University
Download the .zip archive of the patcher files here.
A couple loops can also be found here.
Concept and inspiration
My aim for this project was to create a sequencer/looper Max/MSP patch that had some mind of its own. In listening to beat-oriented electronic music, I regularly fall back on that which is the most rhythmically interesting, for both its compositional and production value. I sought to create a 16-step sequencer that is based on the playback of “slices” of a sound-file. These slices would be able to be re-ordered for playback, and each step’s parameters would be able to be modulated by ranges of random values. It was also critical for the sequencer’s internal communication to be local in scope, such that many can be run simultaneously. This much is now functional, but a number of features that I imagined I’d have incorporated did not make it into this initial “release.” (Hence ‘version 0.1’)
Some time ago I stumbled upon the modsquad-redux patch in the Max examples folder, and from this I borrowed the basic engine concept: the playback of a sound-file loop, loaded into a buffer, can be controlled by another buffer which serves as an index. The index controls what portion of the loop buffer will be played back in the duration of one period. The two waveform displays below show a default index buffer (which ramps from 0.0 to 1.0 over the loop length), and one which has certain slices shifted, resulting in non-linear playback. (From this point on, I’ll refer to a slice as a segment of the original loop, and a step as one of the 16 possible points in time for one period of the sequencer. There are always 16 steps, but each step can play any one slice, or nothing at all.)
A wave~ object reads this buffer, and multiplies the output by the sample length of the actual loop in order to drive an index~ that reads the loop. This concept is simple enough. This dependence on an index buffer makes things much simpler for the Max scheduler, as everything is kept in the signal domain. This also means, however, that every change to step parameters means that sections (or the entirety) of the buffer have to be re-written sample-by-sample, using series of uzi’s and expressions.
Implementation
This is where wave~ comes in very handy. wave~ by default interpolates between sample values, meaning that we do not have to deal with an index buffer whose sample count mirrors that of the loop file. Constantly editing sample-by-sample a buffer of more than 120,000 samples would greatly slow the interface and functionality of the patch. wave~’s on-the-fly interpolation allows us to use a much smaller buffer to store the lookup data. In this case, I’m using 1024 samples. The reason I am not using an even smaller buffer is that I want to maintain a certain amount of resolution in the index, as one the important step parameters is repetitions per step.
I initially attempted a design that cut a loop into a separate buffer for every step (16 in total), which would allow for easy visual editing of each buffer’s loop points via the waveform~ object. I quickly realized that the timing of these buffers would still need to be controlled by MSP objects, as it would take special effort to use the Max scheduler to sequence these responsively enough. (My results were laggy).
While the Max preset object is simple and effective for many situations, I do not find it useful enough in dealing with sequencer data. I instead used a collection, which simply contains the individual step parameters in separate indices. As objects need to retrieve this data, the list for the corresponding slice is parsed to obtain one or more nth elements.
1, slice 15 reps 1. repsPercent 0 repsMod 1. reverse 0 pan 0.564 panMod 0.999;
2, slice 14 reps 1. repsPercent 0 repsMod 1. reverse 0 pan 0.5 panMod 0.999;
3, slice 3 reps 1.91 repsPercent 0 repsMod 1. reverse 0 pan 0.5 panMod 0.999;
4, slice 4 reps 1.72 repsPercent 0 repsMod 1. reverse 0 pan 0.5 panMod 0.999;
5, slice 4 reps 1.33 repsPercent 0 repsMod 1. reverse 0 pan 0.5 panMod 0.999;
6, slice 0 reps 1.68 repsPercent 0 repsMod 1. reverse 0 pan 0.5 panMod 0.961;
7, slice 1 reps 1. repsPercent 0 repsMod 1. reverse 0 pan 0.5 panMod 0.999;
8, slice 11 reps 2.08 repsPercent 0 repsMod 1. reverse 0 pan 0.5 panMod 0.935;
9, slice 7 reps 1.69 repsPercent 100 repsMod 7. reverse 0 pan 0.5 panMod 0.961;
10, slice 8 reps 1.88 repsPercent 0 repsMod 1. reverse 1 pan 0.463 panMod 0.98;
11, slice 9 reps 1.39 repsPercent 100 repsMod 4. reverse 0 pan 0.5 panMod 0.999;
12, slice 1 reps 1. repsPercent 85 repsMod 6. reverse 0 pan 0.83 panMod 0.965;
13, slice 0 reps 1.7 repsPercent 52 repsMod 1. reverse 0 pan 0.5 panMod 0.635;
14, slice 3 reps 1.43 repsPercent 0 repsMod 1. reverse 0 pan 0.163 panMod 0.43;
15, slice 2 reps 2.15 repsPercent 0 repsMod 6. reverse 0 pan 0.444 panMod 0.456;
16, slice 1 reps 1.6 repsPercent 0 repsMod 6. reverse 0 pan 0.47 panMod 0.456;
This data looks like this in a buffer:

A future implementation will also store global data in this collection.
Luckily, the most important component required the least amount of “babysitting” throughout the development so far: the buffer writer/editor subpatch. (called for the moment ‘doIt’). Every edit to a step parameter triggers this subpatch to rewrite the index data for that step. For the moment, only two parameters actually call for selective and immediate index buffer editing: Reverse toggle, and ‘reps’ (repetitions), which squishes the slice playback into the step by a factor of a float-value. Unfortunately values below 1.0 don’t work at the moment.
Pan values are obviously not written to the index buffer, and are instead processed immediately before the output stage. This is not the most efficient implementation possible. A better way would be to store this information in another buffer which would drive a mixer stage before the final output.
The repetitions can be modulated by a range defined by repsMod, and the probability of this process occurring is defined by repsPercent. Panning can also be modulated, although not according to a probability.
A significant issue of using the interpolating wave~ is that this interpolation also occurs in between the abrupt changes in slices. It may not look like it anything is happening in the index buffer, but if, for example, slice 14 jumps to slice 1, wave~’s index when multiplied by the sample length will race across the likes of 80000 samples in a very short time, producing glitches and pops. I tried using a change~ in conjunct with a gate~ to send a value of 0. as wave~ interpolates downwards, but I cannot figure out why this does not make a difference, as the gate~ functions properly if I control it manually. Someone on the max list has, rightfully so, had success with this solution.
Aesthetic and future direction
I created this patch with the hopes of emulating a very particular style of electronic music. I intended this to be able to perform a number of the compositional trademarks used in genres that could be labeled as ‘drill ‘n bass’, ‘gabber’ or ‘idm’. I use these labels with caution, the body of work that has been produced under these genre definitions all too often lacks a great deal of imagination. At the same time, a few inspiring producers/composers/performers/artists have demonstrated in their work an amount of manual editing that boggles the mind. This music has been produced with such attention to detail and an evident human touch that it would be a serious challenge to generate it on-the-fly.
In my listening to existing music of this genre, I focus a considerable amount on the “sonic style” of the production. There is certainly a movement of 80’s-90’s media nostalgia going on in mainstream music (Nintendo beats, etc.), and some of I do find appealing (and much of it I don’t). All the same, while production art that pumps “synth-vox” samples from Commodore 64 games through dirty, free VST plugins may be cheap and easy to do, I am still intrigued by the “cross-generational” result. It also reminds me of how much I longed to hear how my Wolfenstein3D would sound if I had whatever it was that was called an “Adlib.”
My goal was to build up a vocabulary of rhythmic tools that could be applied according to higher-level rules that are obeyed automatically, and/or executed selectively by someone “performing” the patch. As an example: a trigger that would override the current index buffer and repeat one slice, slowing it by a fraction reiteratively over the course of a loop. Features like these would need to depend on a tight but flexible looping/slicing system. There is room for optimization with regards to the editing of the index buffer, and also how the GUI responds to this. I would like to continue experimenting with different index buffer sizes and also the use of bpatchers for the step editors. (unlock the step editor if you dare! cobweb!)
At this point it is difficult to say whether the patch is more suitable for performance or compositional aid. I threw the interface together quickly for the sake of having a finished product, but I’m not really interested in making something that’s so straight-forward that anyone could use it without instructions. My enjoyment of music that exploits its own stages of production DSP also applies to the use of my own patches: I like breaking them to produce unexpected results. This may place a kind of “software watermark” on the audio, but it’s considerably more ambiguous than a Reason watermark!
Closing remarks
Some things I’d like to implement:
fix the wave~ interpolation zippering!
global step processes, which could be used in conjunction with a semi-intelligent histogram system (perhaps do some research in algorithmic composition)
pitch-shifting functions, both more user-friendly “tape-style” and actual pitch-shifting processors (fiddle~-style, optimized for beats)
a real mixing environment: steps and loopers can be routed to sends containing stacks of basic effects
more live-friendly performance features in the main window: solo buttons, many more keyboard functions for triggering/resetting loops, and MIDI control functions (it would be great if it were possible to control the entire thing with a QWERTY keyboard)
Rewire implementation
The amount of progress I have made over a couple months is substantial, but nonetheless I am only beginning to understand the potential scope of this project. As I began to pull together the components of the patch over just a few days, the need for elegance in interface design became obvious. Although it feels now as though the patch is just beginning, already I can think of ways in which I’d like it to run more efficiently. I’m quite excited by the development of this project and hope to keep working at it, at least until I get bored of it.
Thanks again to Atau Tanaka and his ModSquadRedux patch.
November 29, 2005.